Compare commits

..

86 Commits

Author SHA1 Message Date
329c5c1af8 scan: fix deprecated shuffle parameter in python 3.11 2024-01-04 10:41:44 +08:00
2eddbadfef ad53xx: fix load() references in documentation 2023-10-16 07:56:15 +02:00
cc81464f53 Fix type annotations with mixed tuples
The type checker/inferer visits every node in an AST tree, including
function return annotations. This means for a function definition like

    def f() -> TTuple([TInt32, TBool]):
      ...

We attempt to type check the list [TInt32, TBool], which generates the
unification constraint builtins.TBool ~ builtins.TInt. This causes an
internal error due to compiler weirdness.

We can avoid this by just nulling-out the return annotation in the
embedding stage. The return type isn't actually used anywhere (it's
extracted via the inspect module instead), so this is entirely safe.

Arguments aren't affected by this, as we already nulled out the
annotation (see visit_arg in embedding.py).

Signed-off-by: Jonathan Coates <jonathan.coates@oxionics.com>
2023-09-27 10:27:05 +08:00
David Nadlinger
3f27c76619 compiler: Fix crash on multiple types with the same name
The original fix in 21574bdfa9
was incomplete, as it only addressed the TInstance types, but
not their linked (typ.constructor) TConstructor instances.

This would (potentially among other issues) cause assertion
errors in llvm_ir_generator due to the wrong associated globals
being referenced; see added test case for an example that
previously caused such a crash.

Also modified the name collision detection from O(len(type_map))
(so quadratic overall in the number of custom types) to cache
names in sets for O(1) lookup.
2023-09-27 10:26:54 +08:00
93edfebb7e mirny: fix minor doc formatting 2023-09-06 15:52:56 +08:00
568ef2336c mirny: fix doc formatting 2023-09-05 17:00:46 +08:00
f6ce6bb806 i2c: fix doc formatting 2023-09-05 17:00:46 +08:00
21c6f57ce1 jsonschema: add kasli_soc HW revision v1.1 2023-07-27 11:05:13 +08:00
928ca50762 Fix panic when receiving empty strings in rpc calls
Receiving an empty string in an RPC call currently panics.

When `length` is zero, a call to the `alloc` function (as implemented in `artiq/firmware/runtime/session.rs`) returns a null pointer. Constructing a `CMutSlice` from a null pointer panics.
A `CMutSlice` consists of a pointer and the length. Rust's documentation of the `core::ptr` module states: "The canonical way to obtain a pointer that is valid for zero-sized accesses is `NonNull::dangling`."
This commits adds a check for the length of a string received in an RPC call. Only for lengths greater than zero a memory allocation is performed. For zero-length strings, a dangling pointer is used.

Test plan:
Invoke the following experiment, which returns an empty string over RPC:
```
class ReturnEmptyString(artiq.experiment.EnvExperiment):
    def build(self):
        self.core: Core = self.get_device("core")

    @kernel
    def run(self):
        x = self.do_rpc()
        print(x)

    @rpc
    def do_rpc(self) -> TStr:
        return ""
```

Signed-off-by: Sven Over (Oxford Ionics) <sven.over@oxionics.com>
2023-07-18 12:01:25 +08:00
2c7b62254e gui/experiments: cast Qt timestamp to int preventing float type error 2023-07-14 16:34:09 +08:00
linuswck
f10c876ed7
kasli-soc: fix GTX initialization
The changes are backported from PR2128.

Org Problem: DRIO cannot establish connections with satellite after updatting
the IBUFDS_GTE2 parameters on commit d6704d30e9.

Description of Changes:
- CPLL Parameters are added.
- CEB signal of IBUFDS_GTE2 is asserted by NOT(OR(stable_clkin, GTX CPLL Locked)).
- Modify the GTX Init FSM so that the PLL Reset and GTX Reset are done in two seperated state.
- Restart of GTX module now only resets GTX transceiver.
2023-07-13 16:37:14 +08:00
6fbfa12e88 gtx_7series_init: GTH -> GTX (NFC) 2023-07-10 11:36:28 +08:00
ad06924daa flake: update dependencies 2023-07-10 11:32:31 +08:00
93e26cacdd rtio_clocking: remove confusing log message 2023-07-10 02:36:34 +00:00
ff976754cf fix missing DIFF_TERM for Sampler and Mirny inputs 2023-06-02 17:21:32 +08:00
d6704d30e9 fix IBUFDS_GTE2 parameters 2023-05-30 11:49:13 +08:00
1315654f0a update dependencies, misoc libunwind cleanup 2023-05-30 11:46:23 +08:00
Charles Baynham
3a9213d5eb worker: Wait until datasets are written before quitting
Avoids a race condition in worker_impl.py where HDF5 dataset saving was
cut off before it finished for large datasets.
2023-05-24 06:58:15 +08:00
c691560fd6 dma: fix off-by-one error in RawSlicer (#2090)
Signed-off-by: Jonathan Coates <jonathan.coates@oxionics.com>
2023-05-23 11:16:24 +08:00
0323946ffb Fix mismatched signatures for the wide interface
Lists are passed by-reference from python code, and so should be
&CSlice<_> not CSlice<_>.

Signed-off-by: Jonathan Coates <jonathan.coates@oxionics.com>
2023-05-19 10:18:30 +08:00
Hartmann Michael (IFAG PSS SIS SCE QSE)
19059a3385 doc: conda installation notes 2023-05-12 17:45:22 +08:00
dbd5a1765d flake: update dependencies 2023-05-01 09:35:38 +08:00
55c8ef588c fix default version string 2023-04-22 09:36:07 +08:00
Ikko Eltociear Ashimine
3acffe8b9f fix typo in comm_analyzer.py
error_occured -> error_occurred
occured -> occurred
2023-04-02 09:27:07 +08:00
1d45bed90a firmware: assume empty config records as removed (#2064)
This will return `KeyNotFound` for empty values, which are produced by `remove` operation

Signed-off-by: Egor Savkin <es@m-labs.hk>
2023-03-13 18:19:27 +08:00
Ikko Eltociear Ashimine
6bf3f53367 fix typo in developing_a_ndsp.rst
occurence -> occurrence
2023-03-11 18:32:49 +08:00
8a2ea578b8 flake: vivado 2022.2 2023-02-20 17:34:25 +08:00
81dbbd08b2 doc: duplicate nixConfig 2023-01-04 15:14:28 +08:00
David Nadlinger
1dd0d3432c firmware: Fix object references in tuples
Sine 8740ec3dd, the alignment() information from
"run-time type information" (i.e. the Tag type) is also
used when sending tuples to the host.
2022-12-19 01:04:01 +00:00
David Nadlinger
e0c7880c77 RELEASE_NOTES: Two typo/formatting fixes 2022-12-18 17:27:49 +00:00
David Nadlinger
c811efd9a7 RELEASE_NOTES: Fix up punctuation 2022-12-18 17:27:44 +00:00
David Nadlinger
5f8eeb47bb firmware: Rename si5324 crystal_{ref -> as_ckin2} [nfc]
This would have made the issue in the pre-740543d4e code
much more obvious (the config option by itself does not
have any effect on the choice of active reference input).
2022-12-18 17:27:38 +00:00
David Nadlinger
1db3a42ad7 firmware: Fix Si5324 initialisation for satellites
Commit 740543d4e2 had unintentionally broken DRTIO
satellites, as si5324::setup is also used there. This
imports setup_si5324_as_synthesizer() from artiq-zynq,
where the input selection was already explicitly done.

GitHub: Fixes #2028.
2022-12-18 17:27:27 +00:00
SingularitySurfer
ce57d6c346 implement pca9539 and runtime io-expander chip selection
better comments and address translation

fix spurious };

unwrap init in runtime and return err instead of panic

propagate error

del unnecessary use

Signed-off-by: SingularitySurfer <Norman_Krackow@gmx.de>
2022-12-14 22:47:20 +08:00
David Nadlinger
fe32104185 master/scheduler: Unbreak submitting from repository
This is a fix-up to commit 2a58981822.
2022-12-14 09:13:55 +08:00
1c39ac8fb4 Scheduler: replace relative path to absolute
Signed-off-by: Egor Savkin <es@m-labs.hk>
2022-12-09 21:51:18 +08:00
19824cefba worker_impl: do not write results without rid (#2020) 2022-12-09 16:21:11 +08:00
David Nadlinger
8f3d06a515 runtime/rtio_clocking: Deduplicate/document input selection [nfc] 2022-12-05 10:11:48 +08:00
David Nadlinger
0775ae1c19 firmware: Fix Kasli v2 runtime rtio_clock selection
SI5324_EXT_REF now only controls the (deprecated) fallbacks
for when the rtio_clock option is not set.
2022-12-05 10:11:11 +08:00
696418c2a9 browser: tolerate missing HDF5 metadata 2022-12-02 16:31:47 +08:00
David Nadlinger
520692073e firmware/runtime: Fix Ext0_Synth0_*to125 log messages 2022-12-02 10:49:07 +08:00
47581e0de9 browser: fix dummy device creation failure on analyze 2022-12-01 17:48:17 +08:00
ec5c1b2478 Revert "language: check_unprocessed_arguments after constructing experiment"
This reverts commit d7240c17fc.
2022-11-30 07:44:57 +08:00
Nico Pulido
d7240c17fc language: check_unprocessed_arguments after constructing experiment
Signed-off-by: Nico Pulido-Mateo <pulido@iqo.uni-hannover.de>
2022-11-29 19:06:31 +08:00
David Nadlinger
75d75cc13c compiler: Add missing sections to kernel linker script
This caused sporadic LoadFaults with LLD 14 and above, as they
happened to lay out the (not otherwise mentioned) GOT/PLT such
that they would overlap with the stack guard page.

LLD does support the --orphan-handling=error option, which
would be useful to avoid similar problems in the future, but
then we'd need to mention all the other misc sections
(symbol table, comments) in the linker script as well.

GitHub: Fixes #1975.
2022-11-25 09:50:18 +08:00
079d57b54d ddb_template: propagate fastino log2_width setting
Signed-off-by: Etienne Wodey <etienne.wodey@aqt.eu>
2022-11-17 10:55:31 +08:00
David Nadlinger
3e7680e45b firmware/rpc_proto: Remove unnecessary cast [nfc] 2022-11-15 11:06:38 +08:00
David Nadlinger
60a2ff3799 firmware/rpc_proto: Fix typo breaking receiving of arrays
This was introduced in 8740ec3dd5.
2022-11-15 11:06:38 +08:00
David Nadlinger
d422de387e firmware/rpc_proto: Fix size/alignment calculation for structs with tail padding
Also factors out duplicate code for (de)serializing
elements of lists and ndarrays, and replaces the rounding
calculations by the well-known, much faster power-of-two-only
bit-twiddling version.

GitHub: Fixes #1934.
2022-11-15 11:06:38 +08:00
David Nadlinger
d73915f904 firmware/ksupport: Include .gcc_except_table (LSDA)
For whatever reason, no language-specific unwind data
was generated for ksupport code so far, but rustc does
emit it for an upcoming refactoring.
2022-11-15 11:06:38 +08:00
David Nadlinger
1ddefaa42f firmware/ksupport: Document rpc_recv alignment requirements [nfc] 2022-11-15 11:06:38 +08:00
David Nadlinger
23a4db494f compiler: Extract maximum alignment from target data layout
In particular, i64/double are actually supposed to be aligned
to their size on RISC-V (at least according to the ELF psABI),
though it is unclear to me whether this actually caused any
issues.
2022-11-15 11:06:38 +08:00
David Nadlinger
a83f330d74 compiler/targets: Fix refactoring leftover for native (host) target
It's unclear whether this actually caused any issues, or why this
wasn't done before (instead just setting the now-removed endianness
flag).
2022-11-15 11:06:06 +08:00
火焚 富良
484c88af24 compiler: fix const str/bytes handling (#1990) 2022-11-11 13:16:42 +08:00
694a3490c6 dashboard: restore connection/version message 2022-10-21 19:17:17 +08:00
10bf8704c1 dashboard: remove incorrect moninj proxy message 2022-10-21 19:13:30 +08:00
Fabian Schwartau
ab8bb9ef31 Fixed two too low delay values in Phaser init
Signed-off-by: Fabian Schwartau <fabian@opencode.eu>
2022-10-19 22:21:47 +02:00
de34aedfa3 flake: use nixpkgs llvmlite 2022-10-19 19:38:15 +08:00
wlph17
0119577c33 flake: set Nix Qt environment variables in development shell
allows applets to run standalone via ``python -m ...`` without requiring the Nix Qt wrapper
2022-10-07 11:32:45 +08:00
ad13e2205d Improve exception reports when exception can't be reconstructed
Artiq assumes that all exceptions raised by the kernel can be constructed with
a single string argument. This isn't always the case. Especially for
exceptions that originated in python and were propagated to the kernel over
rpc.

With out this change a mosek solver failure looks like:
```
ERROR    root:logging_tools.py:41 Terminating with exception (TypeError: __init__() missing 1 required positional argument: 'msg')
Traceback (most recent call last):
  File "/home/mb/.cache/pypoetry/virtualenvs/ion-transport-1-b41LI0-py3.8/lib/python3.8/site-packages/artiq/master/worker_impl.py", line 540, in main
    exp_inst.run()
  File "/home/mb/.cache/pypoetry/virtualenvs/ion-transport-1-b41LI0-py3.8/lib/python3.8/site-packages/artiq/test_tools/experiment.py", line 82, in wrapper
    meth()
  File "/home/mb/.cache/pypoetry/virtualenvs/ion-transport-1-b41LI0-py3.8/lib/python3.8/site-packages/artiq/language/core.py", line 54, in run_on_core
    return getattr(self, arg).run(run_on_core, ((self,) + k_args), k_kwargs)
  File "/home/mb/.cache/pypoetry/virtualenvs/ion-transport-1-b41LI0-py3.8/lib/python3.8/site-packages/artiq/coredevice/core.py", line 152, in run
    self.comm.serve(embedding_map, symbolizer, demangler)
  File "/home/mb/.cache/pypoetry/virtualenvs/ion-transport-1-b41LI0-py3.8/lib/python3.8/site-packages/artiq/coredevice/comm_kernel.py", line 720, in serve
    self._serve_exception(embedding_map, symbolizer, demangler)
  File "/home/mb/.cache/pypoetry/virtualenvs/ion-transport-1-b41LI0-py3.8/lib/python3.8/site-packages/artiq/coredevice/comm_kernel.py", line 699, in _serve_exception
    python_exn = python_exn_type(
TypeError: __init__() missing 1 required positional argument: 'msg'
```

With this change we get:
```
ERROR    root:logging_tools.py:41 Terminating with exception (RuntimeError: Exception type=<class 'mosek.Error'>, which couldn't be reconstructed (__init__() missing 1 required positional argument: 'msg'))
Core Device Traceback:
Traceback (most recent call first):
  File "/home/mb/oxionics/ion-transport/tests/test_end_to_end.py", line 280, in get_transport
    return self.seq.solve()
  File "/home/mb/oxionics/ion-transport/tests/test_end_to_end.py", line 288, in artiq_worker_test_end_to_end.TransportTestScan.run(..., ...) (RA=+0x2e4)
    self.seq.record(self.get_transport(1e-6 + 1e-7 * x))
mosek.Error(27): rescode.err_license_expired(1001): The license has expired.

End of Core Device Traceback
Traceback (most recent call last):
  File "/home/mb/oxionics/artiq/artiq/master/worker_impl.py", line 540, in main
    exp_inst.run()
  File "/home/mb/oxionics/artiq/artiq/test_tools/experiment.py", line 82, in wrapper
    meth()
  File "/home/mb/oxionics/artiq/artiq/language/core.py", line 54, in run_on_core
    return getattr(self, arg).run(run_on_core, ((self,) + k_args), k_kwargs)
  File "/home/mb/oxionics/artiq/artiq/coredevice/core.py", line 152, in run
    self.comm.serve(embedding_map, symbolizer, demangler)
  File "/home/mb/oxionics/artiq/artiq/coredevice/comm_kernel.py", line 732, in serve
    self._serve_exception(embedding_map, symbolizer, demangler)
  File "/home/mb/oxionics/artiq/artiq/coredevice/comm_kernel.py", line 714, in _serve_exception
    raise python_exn
RuntimeError: Exception type=<class 'mosek.Error'>, which couldn't be reconstructed (__init__() missing 1 required positional argument: 'msg')
```

Signed-off-by: Michael Birtwell <michael.birtwell@oxionics.com>
2022-09-26 20:26:44 +08:00
40df2a6526 dashboard moninj: add tooltip for off button 2022-09-19 10:22:49 +08:00
247f10176a dashboard moninj: check if ad9910 was init 2022-09-19 10:22:49 +08:00
72b92f559d moninj: fix ad9914 behavior, comment cleanup 2022-09-19 10:22:49 +08:00
5581ae15ca moninj: dds inj: extract shared code
detect urukul already init in more than one way
detect ad9912 channel already init
2022-09-19 10:22:49 +08:00
3038639802 afws_client: fix argument order 2022-08-25 13:18:01 +08:00
fanmingyu212
efa514989c doc: updates artiq_flash syntax in developing.rst 2022-08-25 12:57:48 +08:00
9aa81e1234 versioneer: fix default 2022-08-18 14:35:43 +08:00
kk1050
ebe7348a92 dashboard: use break_realtime instead of reset for Urukul set freq (#1940) 2022-08-16 14:02:55 +08:00
5016004d26 dashboard: improve moninj logging 2022-08-12 13:42:21 +08:00
bfab7a7422 kasli: relocate the SatelliteBase Error LED code (#1955) 2022-08-12 13:42:21 +08:00
cc78078
97cba3fd5f kasli: add Error LED to MasterBase and SatelliteBase 2022-08-11 15:08:34 +08:00
Deepskyhunter
eba143a475 dashboard/moninj: make arguments a dict for DDS setters 2022-08-02 17:10:58 +08:00
Alex Wong Tat Hang
2e6ad950b7 gtx_7series: fix IBUFGS_GTE2 buffer parameters
Co-authored-by: topquark12 <aw@m-labs.hk>
2022-08-01 10:23:46 +08:00
30cb821197 manual/installing: fix conda channel url and note 2022-07-25 15:49:35 +02:00
560b7a5448 typo 2022-07-21 11:58:36 +08:00
de6f44467f doc: specify release-7 branch in flake URLs 2022-07-09 12:27:02 +08:00
59dfb9e902 remove beta from version string 2022-07-08 18:20:23 +08:00
2e05a1bd0d Revert "Upgrade smoltcp 0.6.0 -> 0.8.0"
This reverts commit c60de48a30.
2022-07-08 17:58:13 +08:00
d622fb8db7 Revert "DHCP support for core device firmware"
This reverts commit 6ffb1f83ee.
2022-07-08 17:58:00 +08:00
c4a9fa78ee Revert "Prefer DHCP to the built-in static IPs"
This reverts commit 596b9a265c.
2022-07-08 17:57:25 +08:00
3adcf37625 Revert "Ensure that pending data is sent when closing sockets"
This reverts commit 73082d116f.
2022-07-08 17:56:33 +08:00
9941ee3d2a Revert "Use an Ipv4AddrConfig enum instead of the USE_DHCP constant"
This reverts commit 1fe59d27dc.
2022-07-08 17:56:20 +08:00
542a5f934f Revert "Require explicitly closing TcpStreams"
This reverts commit 671453938b.
2022-07-08 17:56:12 +08:00
f1b2a7041a Revert "Centralise all uses of the IPv4 index in net_settings.rs"
This reverts commit 95378cf9c9.
2022-07-08 17:56:04 +08:00
93e82e2201 Revert "Use new ip_addr_storage module instead of net_settings"
This reverts commit 50dbda4f43.
2022-07-08 17:55:57 +08:00
7b72c9e915 remove WRPLL 2022-07-08 17:55:26 +08:00
286 changed files with 14162 additions and 17727 deletions

3
.gitignore vendored
View File

@ -29,7 +29,6 @@ __pycache__/
/repository/
/results
/last_rid.pyon
/dataset_db.mdb
/dataset_db.mdb-lock
/dataset_db.pyon
/device_db*.py
/test*

View File

@ -26,6 +26,7 @@ report if possible:
* Operating System
* ARTIQ version (with recent versions of ARTIQ, run ``artiq_client --version``)
* Version of the gateware and runtime loaded in the core device (in the output of ``artiq_coremgmt -D .... log``)
* If using Conda, output of `conda list`
* Hardware involved

View File

@ -13,7 +13,7 @@ ARTIQ uses FPGA hardware to perform its time-critical tasks. The `Sinara hardwar
ARTIQ is designed to be portable to hardware platforms from different vendors and FPGA manufacturers.
Several different configurations of a `FPGA evaluation kit <https://www.xilinx.com/products/boards-and-kits/ek-k7-kc705-g.html>`_ and of a `Zynq evaluation kit <https://www.xilinx.com/products/boards-and-kits/ek-z7-zc706-g.html>`_ are also used and supported. FPGA platforms can be combined with any number of additional peripherals, either already accessible from ARTIQ or made accessible with little effort.
ARTIQ and its dependencies are available in the form of Nix packages (for Linux) and MSYS2 packages (for Windows). See `the manual <https://m-labs.hk/experiment-control/resources/>`_ for installation instructions.
ARTIQ and its dependencies are available in the form of Nix packages (for Linux) and Conda packages (for Windows and Linux). See `the manual <https://m-labs.hk/experiment-control/resources/>`_ for installation instructions.
Packages containing pre-compiled binary images to be loaded onto the hardware platforms are supplied for each configuration.
Like any open source software ARTIQ can equally be built and installed directly from `source <https://github.com/m-labs/artiq>`_.
@ -29,7 +29,7 @@ Website: https://m-labs.hk/artiq
License
=======
Copyright (C) 2014-2023 M-Labs Limited.
Copyright (C) 2014-2022 M-Labs Limited.
ARTIQ is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by

View File

@ -3,99 +3,6 @@
Release notes
=============
ARTIQ-8 (Unreleased)
--------------------
Highlights:
* New hardware support:
- Support for Shuttler, a 16-channel 125MSPS DAC card intended for ion transport.
Waveform generator and user API are similar to the NIST PDQ.
- Implemented Phaser-servo. This requires recent gateware on Phaser.
- Almazny v1.2 with finer RF switch control.
- Metlino and Sayma support has been dropped due to complications with synchronous RTIO clocking.
- More user LEDs are exposed to RTIO on Kasli.
- Implemented Phaser-MIQRO support. This requires the proprietary Phaser MIQRO gateware
variant from QUARTIQ.
- Sampler: fixed ADC MU to Volt conversion factor for Sampler v2.2+.
For earlier hardware versions, specify the hardware version in the device
database file (e.g. ``"hw_rev": "v2.1"``) to use the correct conversion factor.
* Support for distributed DMA, where DMA is run directly on satellites for corresponding
RTIO events, increasing bandwidth in scenarios with heavy satellite usage.
* Support for subkernels, where kernels are run on satellite device CPUs to offload some
of the processing and RTIO operations.
* CPU (on softcore platforms) and AXI bus (on Zynq) are now clocked synchronously with the RTIO
clock, to facilitate implementation of local processing on DRTIO satellites, and to slightly
reduce RTIO latency.
* Support for DRTIO-over-EEM, used with Shuttler.
* Added channel names to RTIO error messages.
* GUI:
- Implemented Applet Request Interfaces which allow applets to modify datasets and set the
current values of widgets in the dashboard's experiment windows.
- Implemented a new EntryArea widget which allows argument entry widgets to be used in applets.
- The "Close all applets" command (shortcut: Ctrl-Alt-W) now ignores docked applets,
making it a convenient way to clean up after exploratory work without destroying a
carefully arranged default workspace.
- Hotkeys now organize experiment windows in the order they were last interacted with:
+ CTRL+SHIFT+T tiles experiment windows
+ CTRL+SHIFT+C cascades experiment windows
* Persistent datasets are now stored in a LMDB database for improved performance.
* Python's built-in types (such as ``float``, or ``List[...]``) can now be used in type annotations on
kernel functions.
* Full Python 3.10 support.
* MSYS2 packaging for Windows, which replaces Conda. Conda packages are still available to
support legacy installations, but may be removed in a future release.
Breaking changes:
* ``SimpleApplet`` now calls widget constructors with an additional ``ctl`` parameter for control
operations, which includes dataset operations. It can be ignored if not needed. For an example usage,
refer to the ``big_number.py`` applet.
* ``SimpleApplet`` and ``TitleApplet`` now call ``data_changed`` with additional parameters. Derived applets
should change the function signature as below:
::
# SimpleApplet
def data_changed(self, value, metadata, persist, mods)
# SimpleApplet (old version)
def data_changed(self, data, mods)
# TitleApplet
def data_changed(self, value, metadata, persist, mods, title)
# TitleApplet (old version)
def data_changed(self, data, mods, title)
Accesses to the data argument should be replaced as below:
::
data[key][0] ==> persist[key]
data[key][1] ==> value[key]
* The ``ndecimals`` parameter in ``NumberValue`` and ``Scannable`` has been renamed to ``precision``.
Parameters after and including ``scale`` in both constructors are now keyword-only.
Refer to the updated ``no_hardware/arguments_demo.py`` example for current usage.
* Almazny v1.2 is incompatible with the legacy versions and is the default.
To use legacy versions, specify ``almazny_hw_rev`` in the JSON description.
* kasli_generic.py has been merged into kasli.py, and the demonstration designs without JSON descriptions
have been removed. The base classes remain present in kasli.py to support third-party flows without
JSON descriptions.
* Legacy PYON databases should be converted to LMDB with the script below:
::
from sipyco import pyon
import lmdb
old = pyon.load_file("dataset_db.pyon")
new = lmdb.open("dataset_db.mdb", subdir=False, map_size=2**30)
with new.begin(write=True) as txn:
for key, value in old.items():
txn.put(key.encode(), pyon.encode((value, {})).encode())
new.close()
* ``artiq.wavesynth`` has been removed.
ARTIQ-7
-------

View File

@ -1,7 +1,4 @@
import os
def get_rev():
return os.getenv("VERSIONEER_REV", default="unknown")
def get_version():
return os.getenv("VERSIONEER_OVERRIDE", default="8.0+unknown.beta")
return os.getenv("VERSIONEER_OVERRIDE", default="7.0")

23
artiq/afws.pem Normal file
View File

@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIID0zCCArugAwIBAgIUPkNfEUx/uau3z8SD4mgMbCK/DEgwDQYJKoZIhvcNAQEL
BQAweTELMAkGA1UEBhMCSEsxEzARBgNVBAgMClNvbWUtU3RhdGUxFzAVBgNVBAoM
Dk0tTGFicyBMaW1pdGVkMRkwFwYDVQQDDBBuaXhibGQubS1sYWJzLmhrMSEwHwYJ
KoZIhvcNAQkBFhJoZWxwZGVza0BtLWxhYnMuaGswHhcNMjIwMjA2MTA1ODQ0WhcN
MjUwMjA1MTA1ODQ0WjB5MQswCQYDVQQGEwJISzETMBEGA1UECAwKU29tZS1TdGF0
ZTEXMBUGA1UECgwOTS1MYWJzIExpbWl0ZWQxGTAXBgNVBAMMEG5peGJsZC5tLWxh
YnMuaGsxITAfBgkqhkiG9w0BCQEWEmhlbHBkZXNrQG0tbGFicy5oazCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAPWetZhoggPR2ae7waGzv1AQ8NQO3noW
8DofVjusNpX5i/YB0waAr1bm1tALLJoHV2r/gTxujlXCe/L/WG1DLseCf6NO9sHg
t0FLhDpF9kPMWBgauVVLepd2Y2yU1G8eFuEVGnsiQSu0IzsZP5FQBJSyxvxJ+V/L
EW9ox91VGOP9VZR9jqdlYjGhcwClHA/nHe0q1fZq42+9rG466I5yIlNSoa7ilhTU
2C2doxy6Sr6VJYnLEMQqoIF65t3MkKi9iaqN7az0OCrj6XR0P5iKBzUhIgMUd2qs
7Id0XUdbQvaoaRI67vhGkNr+f4rdAUNCDGcbbokuBnmE7/gva6BAABUCAwEAAaNT
MFEwHQYDVR0OBBYEFM2e2FmcytXhKyfC1KEjVJ2mPSy3MB8GA1UdIwQYMBaAFM2e
2FmcytXhKyfC1KEjVJ2mPSy3MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
BQADggEBAKH0z5vlbfTghjYWwd2yEEFBbZx5XxaLHboFQpFpxu9sZoidVs047tco
MOr1py9juiNGGM8G35sw9306f+thDFwqlQfSExUwp5pRQNq+mxglMSF05HWDqBwb
wnItKi/WXpkMQXgpQJFVeflz4B4ZFNlH1UQl5bwacXOM9NM9zO7duCjVXmGE0yxi
VQyApfPQYu9whCSowDYYaA0toJeikMzGfWxhlAH79/2Qmit8KcSCbX1fK/QoRZLa
5NeUi/OlJbBpkgTrfzfMLphmsPWPAVMeUKzqd/vXfG6ZBOZZm6e6sl8RBycBezII
15WekikTE5+T54/E0xiu+zIW/Xhhk14=
-----END CERTIFICATE-----

View File

@ -1,96 +1,22 @@
#!/usr/bin/env python3
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5 import QtWidgets
from artiq.applets.simple import SimpleApplet
from artiq.tools import scale_from_metadata
from artiq.gui.tools import LayoutWidget
class QResponsiveLCDNumber(QtWidgets.QLCDNumber):
doubleClicked = QtCore.pyqtSignal()
def mouseDoubleClickEvent(self, event):
self.doubleClicked.emit()
class QCancellableLineEdit(QtWidgets.QLineEdit):
editCancelled = QtCore.pyqtSignal()
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Escape:
self.editCancelled.emit()
else:
super().keyPressEvent(event)
class NumberWidget(LayoutWidget):
def __init__(self, args, req):
LayoutWidget.__init__(self)
class NumberWidget(QtWidgets.QLCDNumber):
def __init__(self, args):
QtWidgets.QLCDNumber.__init__(self)
self.setDigitCount(args.digit_count)
self.dataset_name = args.dataset
self.req = req
self.metadata = dict()
self.number_area = QtWidgets.QStackedWidget()
self.addWidget(self.number_area, 0, 0)
self.unit_area = QtWidgets.QLabel()
self.unit_area.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTop)
self.addWidget(self.unit_area, 0, 1)
self.lcd_widget = QResponsiveLCDNumber()
self.lcd_widget.setDigitCount(args.digit_count)
self.lcd_widget.doubleClicked.connect(self.start_edit)
self.number_area.addWidget(self.lcd_widget)
self.edit_widget = QCancellableLineEdit()
self.edit_widget.setValidator(QtGui.QDoubleValidator())
self.edit_widget.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.edit_widget.editCancelled.connect(self.cancel_edit)
self.edit_widget.returnPressed.connect(self.confirm_edit)
self.number_area.addWidget(self.edit_widget)
font = QtGui.QFont()
font.setPointSize(60)
self.edit_widget.setFont(font)
unit_font = QtGui.QFont()
unit_font.setPointSize(20)
self.unit_area.setFont(unit_font)
self.number_area.setCurrentWidget(self.lcd_widget)
def start_edit(self):
# QLCDNumber value property contains the value of zero
# if the displayed value is not a number.
self.edit_widget.setText(str(self.lcd_widget.value()))
self.edit_widget.selectAll()
self.edit_widget.setFocus()
self.number_area.setCurrentWidget(self.edit_widget)
def confirm_edit(self):
scale = scale_from_metadata(self.metadata)
val = float(self.edit_widget.text())
val *= scale
self.req.set_dataset(self.dataset_name, val, **self.metadata)
self.number_area.setCurrentWidget(self.lcd_widget)
def cancel_edit(self):
self.number_area.setCurrentWidget(self.lcd_widget)
def data_changed(self, value, metadata, persist, mods):
def data_changed(self, data, mods):
try:
self.metadata = metadata[self.dataset_name]
# This applet will degenerate other scalar types to native float on edit
# Use the dashboard ChangeEditDialog for consistent type casting
val = float(value[self.dataset_name])
scale = scale_from_metadata(self.metadata)
val /= scale
n = float(data[self.dataset_name][1])
except (KeyError, ValueError, TypeError):
val = "---"
unit = self.metadata.get("unit", "")
self.unit_area.setText(unit)
self.lcd_widget.display(val)
n = "---"
self.display(n)
def main():

View File

@ -7,13 +7,13 @@ from artiq.applets.simple import SimpleApplet
class Image(pyqtgraph.ImageView):
def __init__(self, args, req):
def __init__(self, args):
pyqtgraph.ImageView.__init__(self)
self.args = args
def data_changed(self, value, metadata, persist, mods):
def data_changed(self, data, mods):
try:
img = value[self.args.img]
img = data[self.args.img][1]
except KeyError:
return
self.setImage(img)

View File

@ -8,20 +8,20 @@ from artiq.applets.simple import TitleApplet
class HistogramPlot(pyqtgraph.PlotWidget):
def __init__(self, args, req):
def __init__(self, args):
pyqtgraph.PlotWidget.__init__(self)
self.args = args
self.timer = QTimer()
self.timer.setSingleShot(True)
self.timer.timeout.connect(self.length_warning)
def data_changed(self, value, metadata, persist, mods, title):
def data_changed(self, data, mods, title):
try:
y = value[self.args.y]
y = data[self.args.y][1]
if self.args.x is None:
x = None
else:
x = value[self.args.x]
x = data[self.args.x][1]
except KeyError:
return
if x is None:

View File

@ -9,7 +9,7 @@ from artiq.applets.simple import TitleApplet
class XYPlot(pyqtgraph.PlotWidget):
def __init__(self, args, req):
def __init__(self, args):
pyqtgraph.PlotWidget.__init__(self)
self.args = args
self.timer = QTimer()
@ -19,16 +19,16 @@ class XYPlot(pyqtgraph.PlotWidget):
'Error bars': False,
'Fit values': False}
def data_changed(self, value, metadata, persist, mods, title):
def data_changed(self, data, mods, title):
try:
y = value[self.args.y]
y = data[self.args.y][1]
except KeyError:
return
x = value.get(self.args.x, (False, None))
x = data.get(self.args.x, (False, None))[1]
if x is None:
x = np.arange(len(y))
error = value.get(self.args.error, (False, None))
fit = value.get(self.args.fit, (False, None))
error = data.get(self.args.error, (False, None))[1]
fit = data.get(self.args.fit, (False, None))[1]
if not len(y) or len(y) != len(x):
self.mismatch['X values'] = True

View File

@ -22,7 +22,7 @@ def _compute_ys(histogram_bins, histograms_counts):
# pyqtgraph.GraphicsWindow fails to behave like a regular Qt widget
# and breaks embedding. Do not use as top widget.
class XYHistPlot(QtWidgets.QSplitter):
def __init__(self, args, req):
def __init__(self, args):
QtWidgets.QSplitter.__init__(self)
self.resize(1000, 600)
self.setWindowTitle("XY/Histogram")
@ -124,11 +124,11 @@ class XYHistPlot(QtWidgets.QSplitter):
return False
return True
def data_changed(self, value, metadata, persist, mods):
def data_changed(self, data, mods):
try:
xs = value[self.args.xs]
histogram_bins = value[self.args.histogram_bins]
histograms_counts = value[self.args.histograms_counts]
xs = data[self.args.xs][1]
histogram_bins = data[self.args.histogram_bins][1]
histograms_counts = data[self.args.histograms_counts][1]
except KeyError:
return
if len(xs) != histograms_counts.shape[0]:

View File

@ -6,18 +6,18 @@ from artiq.applets.simple import SimpleApplet
class ProgressWidget(QtWidgets.QProgressBar):
def __init__(self, args, req):
def __init__(self, args):
QtWidgets.QProgressBar.__init__(self)
self.setMinimum(args.min)
self.setMaximum(args.max)
self.dataset_value = args.value
def data_changed(self, value, metadata, persist, mods):
def data_changed(self, data, mods):
try:
val = round(value[self.dataset_value])
value = round(data[self.dataset_value][1])
except (KeyError, ValueError, TypeError):
val = 0
self.setValue(val)
value = 0
self.setValue(value)

View File

@ -7,112 +7,13 @@ import string
from qasync import QEventLoop, QtWidgets, QtCore
from sipyco.sync_struct import Subscriber, process_mod
from sipyco.pc_rpc import AsyncioClient as RPCClient
from sipyco import pyon
from sipyco.pipe_ipc import AsyncioChildComm
from artiq.language.scan import ScanObject
logger = logging.getLogger(__name__)
class _AppletRequestInterface:
def __init__(self):
raise NotImplementedError
def set_dataset(self, key, value, unit=None, scale=None, precision=None, persist=None):
"""
Set a dataset.
See documentation of ``artiq.language.environment.set_dataset``.
"""
raise NotImplementedError
def mutate_dataset(self, key, index, value):
"""
Mutate a dataset.
See documentation of ``artiq.language.environment.mutate_dataset``.
"""
raise NotImplementedError
def append_to_dataset(self, key, value):
"""
Append to a dataset.
See documentation of ``artiq.language.environment.append_to_dataset``.
"""
raise NotImplementedError
def set_argument_value(self, expurl, name, value):
"""
Temporarily set the value of an argument in a experiment in the dashboard.
The value resets to default value when recomputing the argument.
:param expurl: Experiment URL identifying the experiment in the dashboard. Example: 'repo:ArgumentsDemo'.
:param name: Name of the argument in the experiment.
:param value: Object representing the new temporary value of the argument. For ``Scannable`` arguments, this parameter
should be a ``ScanObject``. The type of the ``ScanObject`` will be set as the selected type when this function is called.
"""
raise NotImplementedError
class AppletRequestIPC(_AppletRequestInterface):
def __init__(self, ipc):
self.ipc = ipc
def set_dataset(self, key, value, unit=None, scale=None, precision=None, persist=None):
metadata = {}
if unit is not None:
metadata["unit"] = unit
if scale is not None:
metadata["scale"] = scale
if precision is not None:
metadata["precision"] = precision
self.ipc.set_dataset(key, value, metadata, persist)
def mutate_dataset(self, key, index, value):
mod = {"action": "setitem", "path": [key, 1], "key": index, "value": value}
self.ipc.update_dataset(mod)
def append_to_dataset(self, key, value):
mod = {"action": "append", "path": [key, 1], "x": value}
self.ipc.update_dataset(mod)
def set_argument_value(self, expurl, name, value):
if isinstance(value, ScanObject):
value = value.describe()
self.ipc.set_argument_value(expurl, name, value)
class AppletRequestRPC(_AppletRequestInterface):
def __init__(self, loop, dataset_ctl):
self.loop = loop
self.dataset_ctl = dataset_ctl
self.background_tasks = set()
def _background(self, coro, *args, **kwargs):
task = self.loop.create_task(coro(*args, **kwargs))
self.background_tasks.add(task)
task.add_done_callback(self.background_tasks.discard)
def set_dataset(self, key, value, unit=None, scale=None, precision=None, persist=None):
metadata = {}
if unit is not None:
metadata["unit"] = unit
if scale is not None:
metadata["scale"] = scale
if precision is not None:
metadata["precision"] = precision
self._background(self.dataset_ctl.set, key, value, metadata=metadata, persist=persist)
def mutate_dataset(self, key, index, value):
mod = {"action": "setitem", "path": [key, 1], "key": index, "value": value}
self._background(self.dataset_ctl.update, mod)
def append_to_dataset(self, key, value):
mod = {"action": "append", "path": [key, 1], "x": value}
self._background(self.dataset_ctl.update, mod)
class AppletIPCClient(AsyncioChildComm):
def set_close_cb(self, close_cb):
self.close_cb = close_cb
@ -163,30 +64,13 @@ class AppletIPCClient(AsyncioChildComm):
exc_info=True)
self.close_cb()
def subscribe(self, datasets, init_cb, mod_cb, dataset_prefixes=[], *, loop):
def subscribe(self, datasets, init_cb, mod_cb, dataset_prefixes=[]):
self.write_pyon({"action": "subscribe",
"datasets": datasets,
"dataset_prefixes": dataset_prefixes})
self.init_cb = init_cb
self.mod_cb = mod_cb
self.listen_task = loop.create_task(self.listen())
def set_dataset(self, key, value, metadata, persist=None):
self.write_pyon({"action": "set_dataset",
"key": key,
"value": value,
"metadata": metadata,
"persist": persist})
def update_dataset(self, mod):
self.write_pyon({"action": "update_dataset",
"mod": mod})
def set_argument_value(self, expurl, name, value):
self.write_pyon({"action": "set_argument_value",
"expurl": expurl,
"name": name,
"value": value})
asyncio.ensure_future(self.listen())
class SimpleApplet:
@ -208,11 +92,8 @@ class SimpleApplet:
"for dataset notifications "
"(ignored in embedded mode)")
group.add_argument(
"--port-notify", default=3250, type=int,
help="TCP port to connect to for notifications (ignored in embedded mode)")
group.add_argument(
"--port-control", default=3251, type=int,
help="TCP port to connect to for control (ignored in embedded mode)")
"--port", default=3250, type=int,
help="TCP port to connect to")
self._arggroup_datasets = self.argparser.add_argument_group("datasets")
@ -251,21 +132,8 @@ class SimpleApplet:
if self.embed is not None:
self.ipc.close()
def req_init(self):
if self.embed is None:
dataset_ctl = RPCClient()
self.loop.run_until_complete(dataset_ctl.connect_rpc(
self.args.server, self.args.port_control, "master_dataset_db"))
self.req = AppletRequestRPC(self.loop, dataset_ctl)
else:
self.req = AppletRequestIPC(self.ipc)
def req_close(self):
if self.embed is None:
self.req.dataset_ctl.close_rpc()
def create_main_widget(self):
self.main_widget = self.main_widget_class(self.args, self.req)
self.main_widget = self.main_widget_class(self.args)
if self.embed is not None:
self.ipc.set_close_cb(self.main_widget.close)
if os.name == "nt":
@ -321,12 +189,7 @@ class SimpleApplet:
return False
def emit_data_changed(self, data, mod_buffer):
persist = dict()
value = dict()
metadata = dict()
for k, d in data.items():
persist[k], value[k], metadata[k] = d
self.main_widget.data_changed(value, metadata, persist, mod_buffer)
self.main_widget.data_changed(data, mod_buffer)
def flush_mod_buffer(self):
self.emit_data_changed(self.data, self.mod_buffer)
@ -341,8 +204,8 @@ class SimpleApplet:
self.mod_buffer.append(mod)
else:
self.mod_buffer = [mod]
self.loop.call_later(self.args.update_delay,
self.flush_mod_buffer)
asyncio.get_event_loop().call_later(self.args.update_delay,
self.flush_mod_buffer)
else:
self.emit_data_changed(self.data, [mod])
@ -351,11 +214,10 @@ class SimpleApplet:
self.subscriber = Subscriber("datasets",
self.sub_init, self.sub_mod)
self.loop.run_until_complete(self.subscriber.connect(
self.args.server, self.args.port_notify))
self.args.server, self.args.port))
else:
self.ipc.subscribe(self.datasets, self.sub_init, self.sub_mod,
dataset_prefixes=self.dataset_prefixes,
loop=self.loop)
dataset_prefixes=self.dataset_prefixes)
def unsubscribe(self):
if self.embed is None:
@ -367,16 +229,12 @@ class SimpleApplet:
try:
self.ipc_init()
try:
self.req_init()
self.create_main_widget()
self.subscribe()
try:
self.create_main_widget()
self.subscribe()
try:
self.loop.run_forever()
finally:
self.unsubscribe()
self.loop.run_forever()
finally:
self.req_close()
self.unsubscribe()
finally:
self.ipc_close()
finally:
@ -415,9 +273,4 @@ class TitleApplet(SimpleApplet):
title = self.args.title
else:
title = None
persist = dict()
value = dict()
metadata = dict()
for k, d in data.items():
persist[k], value[k], metadata[k] = d
self.main_widget.data_changed(value, metadata, persist, mod_buffer, title)
self.main_widget.data_changed(data, mod_buffer, title)

View File

@ -20,46 +20,11 @@ class Model(DictSyncTreeSepModel):
DictSyncTreeSepModel.__init__(self, ".", ["Dataset", "Value"], init)
def convert(self, k, v, column):
return short_format(v[1], v[2])
class DatasetCtl:
def __init__(self, master_host, master_port):
self.master_host = master_host
self.master_port = master_port
async def _execute_rpc(self, op_name, key_or_mod, value=None, persist=None, metadata=None):
logger.info("Starting %s operation on %s", op_name, key_or_mod)
try:
remote = RPCClient()
await remote.connect_rpc(self.master_host, self.master_port,
"master_dataset_db")
try:
if op_name == "set":
await remote.set(key_or_mod, value, persist, metadata)
elif op_name == "update":
await remote.update(key_or_mod)
else:
logger.error("Invalid operation: %s", op_name)
return
finally:
remote.close_rpc()
except:
logger.error("Failed %s operation on %s", op_name,
key_or_mod, exc_info=True)
else:
logger.info("Finished %s operation on %s", op_name,
key_or_mod)
async def set(self, key, value, persist=None, metadata=None):
await self._execute_rpc("set", key, value, persist, metadata)
async def update(self, mod):
await self._execute_rpc("update", mod)
return short_format(v[1])
class DatasetsDock(QtWidgets.QDockWidget):
def __init__(self, dataset_sub, dataset_ctl):
def __init__(self, datasets_sub, master_host, master_port):
QtWidgets.QDockWidget.__init__(self, "Datasets")
self.setObjectName("Datasets")
self.setFeatures(QtWidgets.QDockWidget.DockWidgetMovable |
@ -97,9 +62,10 @@ class DatasetsDock(QtWidgets.QDockWidget):
self.table.addAction(upload_action)
self.set_model(Model(dict()))
dataset_sub.add_setmodel_callback(self.set_model)
datasets_sub.add_setmodel_callback(self.set_model)
self.dataset_ctl = dataset_ctl
self.master_host = master_host
self.master_port = master_port
def _search_datasets(self):
if hasattr(self, "table_model_filter"):
@ -116,14 +82,30 @@ class DatasetsDock(QtWidgets.QDockWidget):
self.table_model_filter.setSourceModel(self.table_model)
self.table.setModel(self.table_model_filter)
async def _upload_dataset(self, name, value,):
logger.info("Uploading dataset '%s' to master...", name)
try:
remote = RPCClient()
await remote.connect_rpc(self.master_host, self.master_port,
"master_dataset_db")
try:
await remote.set(name, value)
finally:
remote.close_rpc()
except:
logger.error("Failed uploading dataset '%s'",
name, exc_info=True)
else:
logger.info("Finished uploading dataset '%s'", name)
def upload_clicked(self):
idx = self.table.selectedIndexes()
if idx:
idx = self.table_model_filter.mapToSource(idx[0])
key = self.table_model.index_to_key(idx)
if key is not None:
persist, value, metadata = self.table_model.backing_store[key]
asyncio.ensure_future(self.dataset_ctl.set(key, value, metadata=metadata))
persist, value = self.table_model.backing_store[key]
asyncio.ensure_future(self._upload_dataset(key, value))
def save_state(self):
return bytes(self.table.header().saveState())

View File

@ -10,14 +10,22 @@ import h5py
from sipyco import pyon
from artiq import __artiq_dir__ as artiq_dir
from artiq.gui.tools import (LayoutWidget, WheelFilter,
log_level_to_name, get_open_file_name)
from artiq.gui.tools import LayoutWidget, log_level_to_name, get_open_file_name
from artiq.gui.entries import procdesc_to_entry
from artiq.master.worker import Worker, log_worker_exception
logger = logging.getLogger(__name__)
class _WheelFilter(QtCore.QObject):
def eventFilter(self, obj, event):
if (event.type() == QtCore.QEvent.Wheel and
event.modifiers() != QtCore.Qt.NoModifier):
event.ignore()
return True
return False
class _ArgumentEditor(QtWidgets.QTreeWidget):
def __init__(self, dock):
QtWidgets.QTreeWidget.__init__(self)
@ -38,7 +46,7 @@ class _ArgumentEditor(QtWidgets.QTreeWidget):
self.setStyleSheet("QTreeWidget {background: " +
self.palette().midlight().color().name() + " ;}")
self.viewport().installEventFilter(WheelFilter(self.viewport(), True))
self.viewport().installEventFilter(_WheelFilter(self.viewport()))
self._groups = dict()
self._arg_to_widgets = dict()
@ -370,9 +378,9 @@ class _ExperimentDock(QtWidgets.QMdiSubWindow):
class LocalDatasetDB:
def __init__(self, dataset_sub):
self.dataset_sub = dataset_sub
dataset_sub.add_setmodel_callback(self.init)
def __init__(self, datasets_sub):
self.datasets_sub = datasets_sub
datasets_sub.add_setmodel_callback(self.init)
def init(self, data):
self._data = data
@ -381,11 +389,11 @@ class LocalDatasetDB:
return self._data.backing_store[key][1]
def update(self, mod):
self.dataset_sub.update(mod)
self.datasets_sub.update(mod)
class ExperimentsArea(QtWidgets.QMdiArea):
def __init__(self, root, dataset_sub):
def __init__(self, root, datasets_sub):
QtWidgets.QMdiArea.__init__(self)
self.pixmap = QtGui.QPixmap(os.path.join(
artiq_dir, "gui", "logo_ver.svg"))
@ -394,7 +402,7 @@ class ExperimentsArea(QtWidgets.QMdiArea):
self.open_experiments = []
self._ddb = LocalDatasetDB(dataset_sub)
self._ddb = LocalDatasetDB(datasets_sub)
self.worker_handlers = {
"get_device_db": lambda: {},
@ -508,9 +516,5 @@ class ExperimentsArea(QtWidgets.QMdiArea):
self.open_experiments.append(dock)
return dock
def set_argument_value(self, expurl, name, value):
logger.warning("Unable to set argument '%s', dropping change. "
"'set_argument_value' not supported in browser.", name)
def on_dock_closed(self, dock):
self.open_experiments.remove(dock)

View File

@ -106,7 +106,7 @@ class Hdf5FileSystemModel(QtWidgets.QFileSystemModel):
start_time = datetime.fromtimestamp(h5["start_time"][()]) if "start_time" in h5 else "<none>"
v = ("artiq_version: {}\nrepo_rev: {}\nfile: {}\n"
"class_name: {}\nrid: {}\nstart_time: {}").format(
h5["artiq_version"].asstr()[()] if "artiq_version" in h5 else "<none>",
h5["artiq_version"][()] if "artiq_version" in h5 else "<none>",
expid.get("repo_rev", "<none>"),
expid.get("file", "<none>"), expid.get("class_name", "<none>"),
h5["rid"][()] if "rid" in h5 else "<none>", start_time)
@ -178,7 +178,7 @@ class FilesDock(QtWidgets.QDockWidget):
expid = pyon.decode(f["expid"][()]) if "expid" in f else dict()
start_time = datetime.fromtimestamp(f["start_time"][()]) if "start_time" in f else "<none>"
v = {
"artiq_version": f["artiq_version"].asstr()[()] if "artiq_version" in f else "<none>",
"artiq_version": f["artiq_version"][()] if "artiq_version" in f else "<none>",
"repo_rev": expid.get("repo_rev", "<none>"),
"file": expid.get("file", "<none>"),
"class_name": expid.get("class_name", "<none>"),
@ -194,9 +194,7 @@ class FilesDock(QtWidgets.QDockWidget):
if "archive" in f:
def visitor(k, v):
if isinstance(v, h5py.Dataset):
# v.attrs is a non-serializable h5py.AttributeManager, need to convert to dict
# See https://docs.h5py.org/en/stable/high/attr.html#h5py.AttributeManager
rd[k] = (True, v[()], dict(v.attrs))
rd[k] = (True, v[()])
f["archive"].visititems(visitor)
@ -206,9 +204,7 @@ class FilesDock(QtWidgets.QDockWidget):
if k in rd:
logger.warning("dataset '%s' is both in archive "
"and outputs", k)
# v.attrs is a non-serializable h5py.AttributeManager, need to convert to dict
# See https://docs.h5py.org/en/stable/high/attr.html#h5py.AttributeManager
rd[k] = (True, v[()], dict(v.attrs))
rd[k] = (True, v[()])
f["datasets"].visititems(visitor)

View File

@ -59,18 +59,19 @@ def build_artiq_soc(soc, argdict):
builder.software_packages = []
builder.add_software_package("bootloader", os.path.join(firmware_dir, "bootloader"))
is_kasli_v1 = isinstance(soc.platform, kasli.Platform) and soc.platform.hw_rev in ("v1.0", "v1.1")
kernel_cpu_type = "vexriscv" if is_kasli_v1 else "vexriscv-g"
builder.add_software_package("libm", cpu_type=kernel_cpu_type)
builder.add_software_package("libprintf", cpu_type=kernel_cpu_type)
builder.add_software_package("libunwind", cpu_type=kernel_cpu_type)
builder.add_software_package("ksupport", os.path.join(firmware_dir, "ksupport"), cpu_type=kernel_cpu_type)
# Generate unwinder for soft float target (ARTIQ runtime)
# If the kernel lacks FPU, then the runtime unwinder is already generated
if not is_kasli_v1:
builder.add_software_package("libunwind")
if not soc.config["DRTIO_ROLE"] == "satellite":
if isinstance(soc, AMPSoC):
kernel_cpu_type = "vexriscv" if is_kasli_v1 else "vexriscv-g"
builder.add_software_package("libm", cpu_type=kernel_cpu_type)
builder.add_software_package("libprintf", cpu_type=kernel_cpu_type)
builder.add_software_package("libunwind", cpu_type=kernel_cpu_type)
builder.add_software_package("ksupport", os.path.join(firmware_dir, "ksupport"), cpu_type=kernel_cpu_type)
# Generate unwinder for soft float target (ARTIQ runtime)
# If the kernel lacks FPU, then the runtime unwinder is already generated
if not is_kasli_v1:
builder.add_software_package("libunwind")
builder.add_software_package("runtime", os.path.join(firmware_dir, "runtime"))
else:
# Assume DRTIO satellite.
builder.add_software_package("satman", os.path.join(firmware_dir, "satman"))
try:
builder.build()

View File

@ -21,19 +21,13 @@ class scoped(object):
set of variables resolved as globals
"""
class remote(object):
"""
:ivar remote_fn: (bool) whether function is ran on a remote device,
meaning arguments are received remotely and return is sent remotely
"""
# Typed versions of untyped nodes
class argT(ast.arg, commontyped):
pass
class ClassDefT(ast.ClassDef):
_types = ("constructor_type",)
class FunctionDefT(ast.FunctionDef, scoped, remote):
class FunctionDefT(ast.FunctionDef, scoped):
_types = ("signature_type",)
class QuotedFunctionDefT(FunctionDefT):
"""
@ -64,7 +58,7 @@ class BinOpT(ast.BinOp, commontyped):
pass
class BoolOpT(ast.BoolOp, commontyped):
pass
class CallT(ast.Call, commontyped, remote):
class CallT(ast.Call, commontyped):
"""
:ivar iodelay: (:class:`iodelay.Expr`)
:ivar arg_exprs: (dict of str to :class:`iodelay.Expr`)

View File

@ -38,9 +38,6 @@ class TInt(types.TMono):
def one():
return 1
def TInt8():
return TInt(types.TValue(8))
def TInt32():
return TInt(types.TValue(32))
@ -247,12 +244,6 @@ def fn_at_mu():
def fn_rtio_log():
return types.TBuiltinFunction("rtio_log")
def fn_subkernel_await():
return types.TBuiltinFunction("subkernel_await")
def fn_subkernel_preload():
return types.TBuiltinFunction("subkernel_preload")
# Accessors
def is_none(typ):
@ -335,7 +326,7 @@ def get_iterable_elt(typ):
# n-dimensional arrays, rather than the n-1 dimensional result of iterating over
# the first axis, which makes the name a bit misleading.
if is_str(typ) or is_bytes(typ) or is_bytearray(typ):
return TInt8()
return TInt(types.TValue(8))
elif types._is_pointer(typ) or is_iterable(typ):
return typ.find()["elt"].find()
else:
@ -351,5 +342,5 @@ def is_allocated(typ):
is_float(typ) or is_range(typ) or
types._is_pointer(typ) or types.is_function(typ) or
types.is_external_function(typ) or types.is_rpc(typ) or
types.is_subkernel(typ) or types.is_method(typ) or
types.is_tuple(typ) or types.is_value(typ))
types.is_method(typ) or types.is_tuple(typ) or
types.is_value(typ))

View File

@ -5,7 +5,6 @@ the references to the host objects and translates the functions
annotated as ``@kernel`` when they are referenced.
"""
import typing
import os, re, linecache, inspect, textwrap, types as pytypes, numpy
from collections import OrderedDict, defaultdict
@ -19,13 +18,6 @@ from . import types, builtins, asttyped, math_fns, prelude
from .transforms import ASTTypedRewriter, Inferencer, IntMonomorphizer, TypedtreePrinter
from .transforms.asttyped_rewriter import LocalExtractor
try:
# From numpy=1.25.0 dispatching for `__array_function__` is done via
# a C wrapper: https://github.com/numpy/numpy/pull/23020
from numpy.core._multiarray_umath import _ArrayFunctionDispatcher
except ImportError:
_ArrayFunctionDispatcher = None
class SpecializedFunction:
def __init__(self, instance_type, host_function):
@ -74,9 +66,7 @@ class EmbeddingMap:
"CacheError",
"SPIError",
"0:ZeroDivisionError",
"0:IndexError",
"UnwrapNoneError",
"SubkernelError"])
"0:IndexError"])
def preallocate_runtime_exception_names(self, names):
for i, name in enumerate(names):
@ -185,22 +175,7 @@ class EmbeddingMap:
obj_typ, _ = self.type_map[type(obj_ref)]
yield obj_id, obj_ref, obj_typ
def subkernels(self):
subkernels = {}
for k, v in self.object_forward_map.items():
if hasattr(v, "artiq_embedded"):
if v.artiq_embedded.destination is not None:
subkernels[k] = v
return subkernels
def has_rpc(self):
return any(filter(
lambda x: (inspect.isfunction(x) or inspect.ismethod(x)) and \
(not hasattr(x, "artiq_embedded") or x.artiq_embedded.destination is None),
self.object_forward_map.values()
))
def has_rpc_or_subkernel(self):
return any(filter(lambda x: inspect.isfunction(x) or inspect.ismethod(x),
self.object_forward_map.values()))
@ -208,7 +183,6 @@ class EmbeddingMap:
class ASTSynthesizer:
def __init__(self, embedding_map, value_map, quote_function=None, expanded_from=None):
self.source = ""
self.source_last_new_line = 0
self.source_buffer = source.Buffer(self.source, "<synthesized>")
self.embedding_map = embedding_map
self.value_map = value_map
@ -227,14 +201,6 @@ class ASTSynthesizer:
return source.Range(self.source_buffer, range_from, range_to,
expanded_from=self.expanded_from)
def _add_iterable(self, fragment):
# Since DILocation points on the beginning of the piece of source
# we don't care if the fragment's end will overflow LLVM's limit.
if len(self.source) - self.source_last_new_line >= 2**16:
fragment = "\\\n" + fragment
self.source_last_new_line = len(self.source) + 2
return self._add(fragment)
def fast_quote_list(self, value):
elts = [None] * len(value)
is_T = False
@ -293,7 +259,7 @@ class ASTSynthesizer:
for index, elt in enumerate(value):
elts[index] = self.quote(elt)
if index < len(value) - 1:
self._add_iterable(", ")
self._add(", ")
return elts
def quote(self, value):
@ -344,28 +310,28 @@ class ASTSynthesizer:
loc=self._add(repr(value)))
elif isinstance(value, str):
return asttyped.StrT(s=value, ctx=None, type=builtins.TStr(),
loc=self._add_iterable(repr(value)))
loc=self._add(repr(value)))
elif isinstance(value, bytes):
return asttyped.StrT(s=value, ctx=None, type=builtins.TBytes(),
loc=self._add_iterable(repr(value)))
loc=self._add(repr(value)))
elif isinstance(value, bytearray):
quote_loc = self._add_iterable('`')
repr_loc = self._add_iterable(repr(value))
unquote_loc = self._add_iterable('`')
quote_loc = self._add('`')
repr_loc = self._add(repr(value))
unquote_loc = self._add('`')
loc = quote_loc.join(unquote_loc)
return asttyped.QuoteT(value=value, type=builtins.TByteArray(), loc=loc)
elif isinstance(value, list):
begin_loc = self._add_iterable("[")
begin_loc = self._add("[")
elts = self.fast_quote_list(value)
end_loc = self._add_iterable("]")
end_loc = self._add("]")
return asttyped.ListT(elts=elts, ctx=None, type=builtins.TList(),
begin_loc=begin_loc, end_loc=end_loc,
loc=begin_loc.join(end_loc))
elif isinstance(value, tuple):
begin_loc = self._add_iterable("(")
begin_loc = self._add("(")
elts = self.fast_quote_list(value)
end_loc = self._add_iterable(")")
end_loc = self._add(")")
return asttyped.TupleT(elts=elts, ctx=None,
type=types.TTuple([e.type for e in elts]),
begin_loc=begin_loc, end_loc=end_loc,
@ -375,9 +341,7 @@ class ASTSynthesizer:
elif inspect.isfunction(value) or inspect.ismethod(value) or \
isinstance(value, pytypes.BuiltinFunctionType) or \
isinstance(value, SpecializedFunction) or \
isinstance(value, numpy.ufunc) or \
(isinstance(value, _ArrayFunctionDispatcher) if
_ArrayFunctionDispatcher is not None else False):
isinstance(value, numpy.ufunc):
if inspect.ismethod(value):
quoted_self = self.quote(value.__self__)
function_type = self.quote_function(value.__func__, self.expanded_from)
@ -486,7 +450,7 @@ class ASTSynthesizer:
return asttyped.QuoteT(value=value, type=instance_type,
loc=loc)
def call(self, callee, args, kwargs, callback=None, remote_fn=False):
def call(self, callee, args, kwargs, callback=None):
"""
Construct an AST fragment calling a function specified by
an AST node `function_node`, with given arguments.
@ -530,7 +494,7 @@ class ASTSynthesizer:
starargs=None, kwargs=None,
type=types.TVar(), iodelay=None, arg_exprs={},
begin_loc=begin_loc, end_loc=end_loc, star_loc=None, dstar_loc=None,
loc=callee_node.loc.join(end_loc), remote_fn=remote_fn)
loc=callee_node.loc.join(end_loc))
if callback is not None:
node = asttyped.CallT(
@ -565,7 +529,7 @@ class StitchingASTTypedRewriter(ASTTypedRewriter):
arg=node.arg, annotation=None,
arg_loc=node.arg_loc, colon_loc=node.colon_loc, loc=node.loc)
def visit_quoted_function(self, node, function, remote_fn):
def visit_quoted_function(self, node, function):
extractor = LocalExtractor(env_stack=self.env_stack, engine=self.engine)
extractor.visit(node)
@ -586,7 +550,7 @@ class StitchingASTTypedRewriter(ASTTypedRewriter):
body=node.body, decorator_list=node.decorator_list,
keyword_loc=node.keyword_loc, name_loc=node.name_loc,
arrow_loc=node.arrow_loc, colon_loc=node.colon_loc, at_locs=node.at_locs,
loc=node.loc, remote_fn=remote_fn)
loc=node.loc)
try:
self.env_stack.append(node.typing_env)
@ -794,7 +758,7 @@ class TypedtreeHasher(algorithm.Visitor):
return hash(tuple(freeze(getattr(node, field_name)) for field_name in fields))
class Stitcher:
def __init__(self, core, dmgr, engine=None, print_as_rpc=True, destination=0, subkernel_arg_types=[]):
def __init__(self, core, dmgr, engine=None, print_as_rpc=True):
self.core = core
self.dmgr = dmgr
if engine is None:
@ -820,19 +784,11 @@ class Stitcher:
self.value_map = defaultdict(lambda: [])
self.definitely_changed = False
self.destination = destination
self.first_call = True
# for non-annotated subkernels:
# main kernel inferencer output with types of arguments
self.subkernel_arg_types = subkernel_arg_types
def stitch_call(self, function, args, kwargs, callback=None):
# We synthesize source code for the initial call so that
# diagnostics would have something meaningful to display to the user.
synthesizer = self._synthesizer(self._function_loc(function.artiq_embedded.function))
# first call of a subkernel will get its arguments from remote (DRTIO)
remote_fn = self.destination != 0
call_node = synthesizer.call(function, args, kwargs, callback, remote_fn=remote_fn)
call_node = synthesizer.call(function, args, kwargs, callback)
synthesizer.finalize()
self.typedtree.append(call_node)
@ -944,10 +900,6 @@ class Stitcher:
return [diagnostic.Diagnostic("note",
"in kernel function here", {},
call_loc)]
elif fn_kind == 'subkernel':
return [diagnostic.Diagnostic("note",
"in subkernel call here", {},
call_loc)]
else:
assert False
else:
@ -967,7 +919,7 @@ class Stitcher:
self._function_loc(function),
notes=self._call_site_note(loc, fn_kind))
self.engine.process(diag)
elif fn_kind == 'rpc' or fn_kind == 'subkernel' and param.default is not inspect.Parameter.empty:
elif fn_kind == 'rpc' and param.default is not inspect.Parameter.empty:
notes = []
notes.append(diagnostic.Diagnostic("note",
"expanded from here while trying to infer a type for an"
@ -986,18 +938,11 @@ class Stitcher:
Inferencer(engine=self.engine).visit(ast)
IntMonomorphizer(engine=self.engine).visit(ast)
return ast.type
elif fn_kind == 'kernel' and self.first_call and self.destination != 0:
# subkernels do not have access to the main kernel code to infer
# arg types - so these are cached and passed onto subkernel
# compilation, to avoid having to annotate them fully
for name, typ in self.subkernel_arg_types:
if param.name == name:
return typ
else:
# Let the rest of the program decide.
return types.TVar()
# Let the rest of the program decide.
return types.TVar()
def _quote_embedded_function(self, function, flags, remote_fn=False):
def _quote_embedded_function(self, function, flags):
# we are now parsing new functions... definitely changed the type
self.definitely_changed = True
@ -1096,7 +1041,7 @@ class Stitcher:
engine=self.engine, prelude=self.prelude,
globals=self.globals, host_environment=host_environment,
quote=self._quote)
function_node = asttyped_rewriter.visit_quoted_function(function_node, embedded_function, remote_fn)
function_node = asttyped_rewriter.visit_quoted_function(function_node, embedded_function)
function_node.flags = flags
# Add it into our typedtree so that it gets inferenced and codegen'd.
@ -1108,6 +1053,9 @@ class Stitcher:
return function_node
def _extract_annot(self, function, annot, kind, call_loc, fn_kind):
if annot is None:
annot = builtins.TNone()
if isinstance(function, SpecializedFunction):
host_function = function.host_function
else:
@ -1121,20 +1069,9 @@ class Stitcher:
if isinstance(embedded_function, str):
embedded_function = host_function
return self._to_artiq_type(
annot,
function=function,
kind=kind,
eval_in_scope=lambda x: eval(x, embedded_function.__globals__),
call_loc=call_loc,
fn_kind=fn_kind)
def _to_artiq_type(
self, annot, *, function, kind: str, eval_in_scope, call_loc: str, fn_kind: str
) -> types.Type:
if isinstance(annot, str):
try:
annot = eval_in_scope(annot)
annot = eval(annot, embedded_function.__globals__)
except Exception:
diag = diagnostic.Diagnostic(
"error",
@ -1144,72 +1081,23 @@ class Stitcher:
notes=self._call_site_note(call_loc, fn_kind))
self.engine.process(diag)
if isinstance(annot, types.Type):
return annot
if not isinstance(annot, types.Type):
diag = diagnostic.Diagnostic("error",
"type annotation for {kind}, '{annot}', is not an ARTIQ type",
{"kind": kind, "annot": repr(annot)},
self._function_loc(function),
notes=self._call_site_note(call_loc, fn_kind))
self.engine.process(diag)
# Convert built-in Python types to ARTIQ ones.
if annot is None:
return builtins.TNone()
elif annot is numpy.int64:
return builtins.TInt64()
elif annot is numpy.int32:
return builtins.TInt32()
elif annot is float:
return builtins.TFloat()
elif annot is bool:
return builtins.TBool()
elif annot is str:
return builtins.TStr()
elif annot is bytes:
return builtins.TBytes()
elif annot is bytearray:
return builtins.TByteArray()
# Convert generic Python types to ARTIQ ones.
generic_ty = typing.get_origin(annot)
if generic_ty is not None:
type_args = typing.get_args(annot)
artiq_args = [
self._to_artiq_type(
x,
function=function,
kind=kind,
eval_in_scope=eval_in_scope,
call_loc=call_loc,
fn_kind=fn_kind)
for x in type_args
]
if generic_ty is list and len(artiq_args) == 1:
return builtins.TList(artiq_args[0])
elif generic_ty is tuple:
return types.TTuple(artiq_args)
# Otherwise report an unknown type and just use a fresh tyvar.
if annot is int:
message = (
"type annotation for {kind}, 'int' cannot be used as an ARTIQ type. "
"Use numpy's int32 or int64 instead."
)
ty = builtins.TInt()
return types.TVar()
else:
message = "type annotation for {kind}, '{annot}', is not an ARTIQ type"
ty = types.TVar()
diag = diagnostic.Diagnostic("error",
message,
{"kind": kind, "annot": repr(annot)},
self._function_loc(function),
notes=self._call_site_note(call_loc, fn_kind))
self.engine.process(diag)
return ty
return annot
def _quote_syscall(self, function, loc):
signature = inspect.signature(function)
arg_types = OrderedDict()
optarg_types = OrderedDict()
for param in signature.parameters.values():
if param.kind != inspect.Parameter.POSITIONAL_OR_KEYWORD:
diag = diagnostic.Diagnostic("error",
@ -1247,40 +1135,6 @@ class Stitcher:
self.functions[function] = function_type
return function_type
def _quote_subkernel(self, function, loc):
if isinstance(function, SpecializedFunction):
host_function = function.host_function
else:
host_function = function
ret_type = builtins.TNone()
signature = inspect.signature(host_function)
if signature.return_annotation is not inspect.Signature.empty:
ret_type = self._extract_annot(host_function, signature.return_annotation,
"return type", loc, fn_kind='subkernel')
arg_types = OrderedDict()
optarg_types = OrderedDict()
for param in signature.parameters.values():
if param.kind != inspect.Parameter.POSITIONAL_OR_KEYWORD:
diag = diagnostic.Diagnostic("error",
"subkernels must only use positional arguments; '{argument}' isn't",
{"argument": param.name},
self._function_loc(function),
notes=self._call_site_note(loc, fn_kind='subkernel'))
self.engine.process(diag)
arg_type = self._type_of_param(function, loc, param, fn_kind='subkernel')
if param.default is inspect.Parameter.empty:
arg_types[param.name] = arg_type
else:
optarg_types[param.name] = arg_type
function_type = types.TSubkernel(arg_types, optarg_types, ret_type,
sid=self.embedding_map.store_object(host_function),
destination=host_function.artiq_embedded.destination)
self.functions[function] = function_type
return function_type
def _quote_rpc(self, function, loc):
if isinstance(function, SpecializedFunction):
host_function = function.host_function
@ -1340,18 +1194,8 @@ class Stitcher:
(host_function.artiq_embedded.core_name is None and
host_function.artiq_embedded.portable is False and
host_function.artiq_embedded.syscall is None and
host_function.artiq_embedded.destination is None and
host_function.artiq_embedded.forbidden is False):
self._quote_rpc(function, loc)
elif host_function.artiq_embedded.destination is not None and \
host_function.artiq_embedded.destination != self.destination:
# treat subkernels as kernels if running on the same device
if not 0 < host_function.artiq_embedded.destination <= 255:
diag = diagnostic.Diagnostic("error",
"subkernel destination must be between 1 and 255 (inclusive)", {},
self._function_loc(host_function))
self.engine.process(diag)
self._quote_subkernel(function, loc)
elif host_function.artiq_embedded.function is not None:
if host_function.__name__ == "<lambda>":
note = diagnostic.Diagnostic("note",
@ -1375,13 +1219,8 @@ class Stitcher:
notes=[note])
self.engine.process(diag)
destination = host_function.artiq_embedded.destination
# remote_fn only for first call in subkernels
remote_fn = destination is not None and self.first_call
self._quote_embedded_function(function,
flags=host_function.artiq_embedded.flags,
remote_fn=remote_fn)
self.first_call = False
flags=host_function.artiq_embedded.flags)
elif host_function.artiq_embedded.syscall is not None:
# Insert a storage-less global whose type instructs the compiler
# to perform a system call instead of a regular call.

View File

@ -706,81 +706,6 @@ class SetLocal(Instruction):
def value(self):
return self.operands[1]
class GetArgFromRemote(Instruction):
"""
An instruction that receives function arguments from remote
(ie. subkernel in DRTIO context)
:ivar arg_name: (string) argument name
:ivar arg_type: argument type
"""
"""
:param arg_name: (string) argument name
:param arg_type: argument type
"""
def __init__(self, arg_name, arg_type, name=""):
assert isinstance(arg_name, str)
super().__init__([], arg_type, name)
self.arg_name = arg_name
self.arg_type = arg_type
def copy(self, mapper):
self_copy = super().copy(mapper)
self_copy.arg_name = self.arg_name
self_copy.arg_type = self.arg_type
return self_copy
def opcode(self):
return "getargfromremote({})".format(repr(self.arg_name))
class GetOptArgFromRemote(GetArgFromRemote):
"""
An instruction that may or may not retrieve an optional function argument
from remote, depending on number of values received by firmware.
:ivar rcv_count: number of received values,
determined by firmware
:ivar index: (integer) index of the current argument,
in reference to remote arguments
"""
"""
:param rcv_count: number of received valuese
:param index: (integer) index of the current argument,
in reference to remote arguments
"""
def __init__(self, arg_name, arg_type, rcv_count, index, name=""):
super().__init__(arg_name, arg_type, name)
self.rcv_count = rcv_count
self.index = index
def copy(self, mapper):
self_copy = super().copy(mapper)
self_copy.rcv_count = self.rcv_count
self_copy.index = self.index
return self_copy
def opcode(self):
return "getoptargfromremote({})".format(repr(self.arg_name))
class SubkernelAwaitArgs(Instruction):
"""
A builtin instruction that takes min and max received messages as operands,
and a list of received types.
:ivar arg_types: (list of types) types of passed arguments (including optional)
"""
"""
:param arg_types: (list of types) types of passed arguments (including optional)
"""
def __init__(self, operands, arg_types, name=None):
assert isinstance(arg_types, list)
self.arg_types = arg_types
super().__init__(operands, builtins.TNone(), name)
class GetAttr(Instruction):
"""
An intruction that loads an attribute from an object,
@ -803,7 +728,7 @@ class GetAttr(Instruction):
typ = obj.type.attributes[attr]
else:
typ = obj.type.constructor.attributes[attr]
if types.is_function(typ) or types.is_rpc(typ) or types.is_subkernel(typ):
if types.is_function(typ) or types.is_rpc(typ):
typ = types.TMethod(obj.type, typ)
super().__init__([obj], typ, name)
self.attr = attr
@ -1265,18 +1190,14 @@ class IndirectBranch(Terminator):
class Return(Terminator):
"""
A return instruction.
:param remote_return: (bool)
marks a return in subkernel context,
where the return value is sent back through DRTIO
"""
"""
:param value: (:class:`Value`) return value
"""
def __init__(self, value, remote_return=False, name=""):
def __init__(self, value, name=""):
assert isinstance(value, Value)
super().__init__([value], builtins.TNone(), name)
self.remote_return = remote_return
def opcode(self):
return "return"

View File

@ -84,8 +84,6 @@ class Module:
constant_hoister.process(self.artiq_ir)
if remarks:
invariant_detection.process(self.artiq_ir)
# for subkernels: main kernel inferencer output, to be passed to further compilations
self.subkernel_arg_types = inferencer.subkernel_arg_types
def build_llvm_ir(self, target):
"""Compile the module to LLVM IR for the specified target."""

View File

@ -37,7 +37,6 @@ def globals():
# ARTIQ decorators
"kernel": builtins.fn_kernel(),
"subkernel": builtins.fn_kernel(),
"portable": builtins.fn_kernel(),
"rpc": builtins.fn_kernel(),
@ -55,8 +54,4 @@ def globals():
# ARTIQ utility functions
"rtio_log": builtins.fn_rtio_log(),
"core_log": builtins.fn_print(),
# ARTIQ subkernel utility functions
"subkernel_await": builtins.fn_subkernel_await(),
"subkernel_preload": builtins.fn_subkernel_preload(),
}

View File

@ -91,12 +91,11 @@ class Target:
tool_ld = "ld.lld"
tool_strip = "llvm-strip"
tool_symbolizer = "llvm-symbolizer"
tool_addr2line = "llvm-addr2line"
tool_cxxfilt = "llvm-cxxfilt"
def __init__(self, subkernel_id=None):
def __init__(self):
self.llcontext = ll.Context()
self.subkernel_id = subkernel_id
def target_machine(self):
lltarget = llvm.Target.from_triple(self.triple)
@ -149,8 +148,7 @@ class Target:
ir.BasicBlock._dump_loc = False
type_printer = types.TypePrinter()
suffix = "_subkernel_{}".format(self.subkernel_id) if self.subkernel_id is not None else ""
_dump(os.getenv("ARTIQ_DUMP_IR"), "ARTIQ IR", suffix + ".txt",
_dump(os.getenv("ARTIQ_DUMP_IR"), "ARTIQ IR", ".txt",
lambda: "\n".join(fn.as_entity(type_printer) for fn in module.artiq_ir))
llmod = module.build_llvm_ir(self)
@ -162,12 +160,12 @@ class Target:
_dump("", "LLVM IR (broken)", ".ll", lambda: str(llmod))
raise
_dump(os.getenv("ARTIQ_DUMP_UNOPT_LLVM"), "LLVM IR (generated)", suffix + "_unopt.ll",
_dump(os.getenv("ARTIQ_DUMP_UNOPT_LLVM"), "LLVM IR (generated)", "_unopt.ll",
lambda: str(llparsedmod))
self.optimize(llparsedmod)
_dump(os.getenv("ARTIQ_DUMP_LLVM"), "LLVM IR (optimized)", suffix + ".ll",
_dump(os.getenv("ARTIQ_DUMP_LLVM"), "LLVM IR (optimized)", ".ll",
lambda: str(llparsedmod))
return llparsedmod
@ -220,8 +218,8 @@ class Target:
# the backtrace entry should point at.
last_inlined = None
offset_addresses = [hex(addr - 1) for addr in addresses]
with RunTool([self.tool_symbolizer, "--addresses", "--functions", "--inlines",
"--demangle", "--output-style=GNU", "--exe={library}"] + offset_addresses,
with RunTool([self.tool_addr2line, "--addresses", "--functions", "--inlines",
"--demangle", "--exe={library}"] + offset_addresses,
library=library) \
as results:
lines = iter(results["__stdout__"].read().rstrip().split("\n"))
@ -258,8 +256,6 @@ class Target:
return backtrace
def demangle(self, names):
if not any(names):
return names
with RunTool([self.tool_cxxfilt] + names) as results:
return results["__stdout__"].read().rstrip().split("\n")
@ -273,37 +269,37 @@ class RV32IMATarget(Target):
triple = "riscv32-unknown-linux"
data_layout = "e-m:e-p:32:32-i64:64-n32-S128"
features = ["m", "a"]
additional_linker_options = ["-m", "elf32lriscv"]
additional_linker_options = []
print_function = "core_log"
now_pinning = True
tool_ld = "ld.lld"
tool_strip = "llvm-strip"
tool_symbolizer = "llvm-symbolizer"
tool_addr2line = "llvm-addr2line"
tool_cxxfilt = "llvm-cxxfilt"
class RV32GTarget(Target):
triple = "riscv32-unknown-linux"
data_layout = "e-m:e-p:32:32-i64:64-n32-S128"
features = ["m", "a", "f", "d"]
additional_linker_options = ["-m", "elf32lriscv"]
additional_linker_options = []
print_function = "core_log"
now_pinning = True
tool_ld = "ld.lld"
tool_strip = "llvm-strip"
tool_symbolizer = "llvm-symbolizer"
tool_addr2line = "llvm-addr2line"
tool_cxxfilt = "llvm-cxxfilt"
class CortexA9Target(Target):
triple = "armv7-unknown-linux-gnueabihf"
data_layout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
features = ["dsp", "fp16", "neon", "vfp3"]
additional_linker_options = ["-m", "armelf_linux_eabi", "--target2=rel"]
additional_linker_options = ["--target2=rel"]
print_function = "core_log"
now_pinning = False
tool_ld = "ld.lld"
tool_strip = "llvm-strip"
tool_symbolizer = "llvm-symbolizer"
tool_addr2line = "llvm-addr2line"
tool_cxxfilt = "llvm-cxxfilt"

View File

@ -30,9 +30,8 @@ def main():
device_db_path = os.path.join(os.path.dirname(sys.argv[1]), "device_db.py")
device_mgr = DeviceManager(DeviceDB(device_db_path))
dataset_db_path = os.path.join(os.path.dirname(sys.argv[1]), "dataset_db.mdb")
dataset_db = DatasetDB(dataset_db_path)
dataset_mgr = DatasetManager()
dataset_db_path = os.path.join(os.path.dirname(sys.argv[1]), "dataset_db.pyon")
dataset_mgr = DatasetManager(DatasetDB(dataset_db_path))
argument_mgr = ProcessArgumentManager({})
@ -69,7 +68,5 @@ def main():
benchmark(lambda: target.strip(elf_shlib),
"Stripping debug information")
dataset_db.close_db()
if __name__ == "__main__":
main()

View File

@ -108,7 +108,6 @@ class ARTIQIRGenerator(algorithm.Visitor):
self.current_args = None
self.current_assign = None
self.current_exception = None
self.current_remote_fn = False
self.break_target = None
self.continue_target = None
self.return_target = None
@ -212,8 +211,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
old_priv_env, self.current_private_env = self.current_private_env, priv_env
self.generic_visit(node)
self.terminate(ir.Return(ir.Constant(None, builtins.TNone()),
remote_return=self.current_remote_fn))
self.terminate(ir.Return(ir.Constant(None, builtins.TNone())))
return self.functions
finally:
@ -296,8 +294,6 @@ class ARTIQIRGenerator(algorithm.Visitor):
old_block, self.current_block = self.current_block, entry
old_globals, self.current_globals = self.current_globals, node.globals_in_scope
old_remote_fn = self.current_remote_fn
self.current_remote_fn = getattr(node, "remote_fn", False)
env_without_globals = \
{var: node.typing_env[var]
@ -330,8 +326,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
self.terminate(ir.Return(result))
elif builtins.is_none(typ.ret):
if not self.current_block.is_terminated():
self.current_block.append(ir.Return(ir.Constant(None, builtins.TNone()),
remote_return=self.current_remote_fn))
self.current_block.append(ir.Return(ir.Constant(None, builtins.TNone())))
else:
if not self.current_block.is_terminated():
if len(self.current_block.predecessors()) != 0:
@ -350,7 +345,6 @@ class ARTIQIRGenerator(algorithm.Visitor):
self.current_block = old_block
self.current_globals = old_globals
self.current_env = old_env
self.current_remote_fn = old_remote_fn
if not is_lambda:
self.current_private_env = old_priv_env
@ -373,8 +367,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
return_value = self.visit(node.value)
if self.return_target is None:
self.append(ir.Return(return_value,
remote_return=self.current_remote_fn))
self.append(ir.Return(return_value))
else:
self.append(ir.SetLocal(self.current_private_env, "$return", return_value))
self.append(ir.Branch(self.return_target))
@ -1205,27 +1198,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
finally:
self.current_assign = old_assign
if types.is_tuple(node.value.type):
assert isinstance(node.slice, ast.Index), \
"Internal compiler error: tuple index should be an Index"
assert isinstance(node.slice.value, ast.Num), \
"Internal compiler error: tuple index should be a constant"
if self.current_assign is not None:
diag = diagnostic.Diagnostic("error",
"cannot assign to a tuple element",
{}, node.loc)
self.engine.process(diag)
index = node.slice.value.n
indexed = self.append(
ir.GetAttr(value, index, name="{}.e{}".format(value.name, index)),
loc=node.loc
)
return indexed
elif isinstance(node.slice, ast.Index):
if isinstance(node.slice, ast.Index):
try:
old_assign, self.current_assign = self.current_assign, None
index = self.visit(node.slice.value)
@ -2531,33 +2504,6 @@ class ARTIQIRGenerator(algorithm.Visitor):
or types.is_builtin(typ, "at_mu"):
return self.append(ir.Builtin(typ.name,
[self.visit(arg) for arg in node.args], node.type))
elif types.is_builtin(typ, "subkernel_await"):
if len(node.args) == 2 and len(node.keywords) == 0:
fn = node.args[0].type
timeout = self.visit(node.args[1])
elif len(node.args) == 1 and len(node.keywords) == 0:
fn = node.args[0].type
timeout = ir.Constant(10_000, builtins.TInt64())
else:
assert False
if types.is_method(fn):
fn = types.get_method_function(fn)
sid = ir.Constant(fn.sid, builtins.TInt32())
if not builtins.is_none(fn.ret):
ret = self.append(ir.Builtin("subkernel_retrieve_return", [sid, timeout], fn.ret))
else:
ret = ir.Constant(None, builtins.TNone())
self.append(ir.Builtin("subkernel_await_finish", [sid, timeout], builtins.TNone()))
return ret
elif types.is_builtin(typ, "subkernel_preload"):
if len(node.args) == 1 and len(node.keywords) == 0:
fn = node.args[0].type
else:
assert False
if types.is_method(fn):
fn = types.get_method_function(fn)
sid = ir.Constant(fn.sid, builtins.TInt32())
return self.append(ir.Builtin("subkernel_preload", [sid], builtins.TNone()))
elif types.is_exn_constructor(typ):
return self.alloc_exn(node.type, *[self.visit(arg_node) for arg_node in node.args])
elif types.is_constructor(typ):
@ -2569,8 +2515,8 @@ class ARTIQIRGenerator(algorithm.Visitor):
node.loc)
self.engine.process(diag)
def _user_call(self, callee, positional, keywords, arg_exprs={}, remote_fn=False):
if types.is_function(callee.type) or types.is_rpc(callee.type) or types.is_subkernel(callee.type):
def _user_call(self, callee, positional, keywords, arg_exprs={}):
if types.is_function(callee.type) or types.is_rpc(callee.type):
func = callee
self_arg = None
fn_typ = callee.type
@ -2585,51 +2531,16 @@ class ARTIQIRGenerator(algorithm.Visitor):
else:
assert False
if types.is_rpc(fn_typ) or types.is_subkernel(fn_typ):
if self_arg is None or types.is_subkernel(fn_typ):
# self is not passed to subkernels by remote
if types.is_rpc(fn_typ):
if self_arg is None:
args = positional
elif self_arg is not None:
else:
args = [self_arg] + positional
for keyword in keywords:
arg = keywords[keyword]
args.append(self.append(ir.Alloc([ir.Constant(keyword, builtins.TStr()), arg],
ir.TKeyword(arg.type))))
elif remote_fn:
assert self_arg is None
assert len(fn_typ.args) >= len(positional)
assert len(keywords) == 0 # no keyword support
args = [None] * fn_typ.arity()
index = 0
# fill in first available args
for arg in positional:
args[index] = arg
index += 1
# remaining args are received through DRTIO
if index < len(args):
# min/max args received remotely (minus already filled)
offset = index
min_args = ir.Constant(len(fn_typ.args)-offset, builtins.TInt8())
max_args = ir.Constant(fn_typ.arity()-offset, builtins.TInt8())
arg_types = list(fn_typ.args.items())[offset:]
arg_type_list = [a[1] for a in arg_types] + [a[1] for a in fn_typ.optargs.items()]
rcvd_count = self.append(ir.SubkernelAwaitArgs([min_args, max_args], arg_type_list))
# obligatory arguments
for arg_name, arg_type in arg_types:
args[index] = self.append(ir.GetArgFromRemote(arg_name, arg_type,
name="ARG.{}".format(arg_name)))
index += 1
# optional arguments
for optarg_name, optarg_type in fn_typ.optargs.items():
idx = ir.Constant(index-offset, builtins.TInt8())
args[index] = \
self.append(ir.GetOptArgFromRemote(optarg_name, optarg_type, rcvd_count, idx))
index += 1
else:
args = [None] * (len(fn_typ.args) + len(fn_typ.optargs))
@ -2715,8 +2626,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
else:
assert False, "Broadcasting for {} arguments not implemented".format(len)
else:
remote_fn = getattr(node, "remote_fn", False)
insn = self._user_call(callee, args, keywords, node.arg_exprs, remote_fn)
insn = self._user_call(callee, args, keywords, node.arg_exprs)
if isinstance(node.func, asttyped.AttributeT):
attr_node = node.func
self.method_map[(attr_node.value.type.find(),

View File

@ -238,7 +238,7 @@ class ASTTypedRewriter(algorithm.Transformer):
body=node.body, decorator_list=node.decorator_list,
keyword_loc=node.keyword_loc, name_loc=node.name_loc,
arrow_loc=node.arrow_loc, colon_loc=node.colon_loc, at_locs=node.at_locs,
loc=node.loc, remote_fn=False)
loc=node.loc)
try:
self.env_stack.append(node.typing_env)
@ -439,9 +439,8 @@ class ASTTypedRewriter(algorithm.Transformer):
def visit_Call(self, node):
node = self.generic_visit(node)
node = asttyped.CallT(type=types.TVar(), iodelay=None, arg_exprs={},
remote_fn=False, func=node.func,
args=node.args, keywords=node.keywords,
node = asttyped.CallT(type=types.TVar(), iodelay=None, arg_exprs={},
func=node.func, args=node.args, keywords=node.keywords,
starargs=node.starargs, kwargs=node.kwargs,
star_loc=node.star_loc, dstar_loc=node.dstar_loc,
begin_loc=node.begin_loc, end_loc=node.end_loc, loc=node.loc)

View File

@ -46,7 +46,6 @@ class Inferencer(algorithm.Visitor):
self.function = None # currently visited function, for Return inference
self.in_loop = False
self.has_return = False
self.subkernel_arg_types = dict()
def _unify(self, typea, typeb, loca, locb, makenotes=None, when=""):
try:
@ -179,7 +178,7 @@ class Inferencer(algorithm.Visitor):
# Convert to a method.
attr_type = types.TMethod(object_type, attr_type)
self._unify_method_self(attr_type, attr_name, attr_loc, loc, value_node.loc)
elif types.is_rpc(attr_type) or types.is_subkernel(attr_type):
elif types.is_rpc(attr_type):
# Convert to a method. We don't have to bother typechecking
# the self argument, since for RPCs anything goes.
attr_type = types.TMethod(object_type, attr_type)
@ -260,31 +259,7 @@ class Inferencer(algorithm.Visitor):
def visit_SubscriptT(self, node):
self.generic_visit(node)
if types.is_tuple(node.value.type):
if (not isinstance(node.slice, ast.Index) or
not isinstance(node.slice.value, ast.Num)):
diag = diagnostic.Diagnostic(
"error", "tuples can only be indexed by a constant", {},
node.slice.loc, []
)
self.engine.process(diag)
return
tuple_type = node.value.type.find()
index = node.slice.value.n
if index < 0 or index >= len(tuple_type.elts):
diag = diagnostic.Diagnostic(
"error",
"index {index} is out of range for tuple of size {size}",
{"index": index, "size": len(tuple_type.elts)},
node.slice.loc, []
)
self.engine.process(diag)
return
self._unify(node.type, tuple_type.elts[index], node.loc, node.value.loc)
elif isinstance(node.slice, ast.Index):
if isinstance(node.slice, ast.Index):
if types.is_tuple(node.slice.value.type):
if types.is_var(node.value.type):
return
@ -1294,55 +1269,6 @@ class Inferencer(algorithm.Visitor):
# Ignored.
self._unify(node.type, builtins.TNone(),
node.loc, None)
elif types.is_builtin(typ, "subkernel_await"):
valid_forms = lambda: [
valid_form("subkernel_await(f: subkernel) -> f return type"),
valid_form("subkernel_await(f: subkernel, timeout: numpy.int64) -> f return type")
]
if 1 <= len(node.args) <= 2:
arg0 = node.args[0].type
if types.is_var(arg0):
pass # undetermined yet
else:
if types.is_method(arg0):
fn = types.get_method_function(arg0)
elif types.is_function(arg0) or types.is_subkernel(arg0):
fn = arg0
else:
diagnose(valid_forms())
self._unify(node.type, fn.ret,
node.loc, None)
if len(node.args) == 2:
arg1 = node.args[1]
if types.is_var(arg1.type):
pass
elif builtins.is_int(arg1.type):
# promote to TInt64
self._unify(arg1.type, builtins.TInt64(),
arg1.loc, None)
else:
diagnose(valid_forms())
else:
diagnose(valid_forms())
elif types.is_builtin(typ, "subkernel_preload"):
valid_forms = lambda: [
valid_form("subkernel_preload(f: subkernel) -> None")
]
if len(node.args) == 1:
arg0 = node.args[0].type
if types.is_var(arg0):
pass # undetermined yet
else:
if types.is_method(arg0):
fn = types.get_method_function(arg0)
elif types.is_function(arg0) or types.is_subkernel(arg0):
fn = arg0
else:
diagnose(valid_forms())
self._unify(node.type, fn.ret,
node.loc, None)
else:
diagnose(valid_forms())
else:
assert False
@ -1381,7 +1307,6 @@ class Inferencer(algorithm.Visitor):
typ_args = typ.args
typ_optargs = typ.optargs
typ_ret = typ.ret
typ_func = typ
else:
typ_self = types.get_method_self(typ)
typ_func = types.get_method_function(typ)
@ -1439,23 +1364,12 @@ class Inferencer(algorithm.Visitor):
other_node=node.args[0])
self._unify(node.type, ret, node.loc, None)
return
if types.is_subkernel(typ_func) and typ_func.sid not in self.subkernel_arg_types:
self.subkernel_arg_types[typ_func.sid] = []
for actualarg, (formalname, formaltyp) in \
zip(node.args, list(typ_args.items()) + list(typ_optargs.items())):
self._unify(actualarg.type, formaltyp,
actualarg.loc, None)
passed_args[formalname] = actualarg.loc
if types.is_subkernel(typ_func):
if types.is_instance(actualarg.type):
# objects cannot be passed to subkernels, as rpc code doesn't support them
diag = diagnostic.Diagnostic("error",
"argument '{name}' of type: {typ} is not supported in subkernels",
{"name": formalname, "typ": actualarg.type},
actualarg.loc, [])
self.engine.process(diag)
self.subkernel_arg_types[typ_func.sid].append((formalname, formaltyp))
for keyword in node.keywords:
if keyword.arg in passed_args:
@ -1486,7 +1400,7 @@ class Inferencer(algorithm.Visitor):
passed_args[keyword.arg] = keyword.arg_loc
for formalname in typ_args:
if formalname not in passed_args and not node.remote_fn:
if formalname not in passed_args:
note = diagnostic.Diagnostic("note",
"the called function is of type {type}",
{"type": types.TypePrinter().name(node.func.type)},

View File

@ -280,7 +280,7 @@ class IODelayEstimator(algorithm.Visitor):
context="as an argument for delay_mu()")
call_delay = value
elif not types.is_builtin(typ):
if types.is_function(typ) or types.is_rpc(typ) or types.is_subkernel(typ):
if types.is_function(typ) or types.is_rpc(typ):
offset = 0
elif types.is_method(typ):
offset = 1
@ -288,7 +288,7 @@ class IODelayEstimator(algorithm.Visitor):
else:
assert False
if types.is_rpc(typ) or types.is_subkernel(typ):
if types.is_rpc(typ):
call_delay = iodelay.Const(0)
else:
delay = typ.find().delay.find()
@ -311,20 +311,13 @@ class IODelayEstimator(algorithm.Visitor):
args[arg_name] = arg_node
free_vars = delay.duration.free_vars()
try:
node.arg_exprs = {
arg: self.evaluate(args[arg], abort=abort,
context="in the expression for argument '{}' "
"that affects I/O delay".format(arg))
for arg in free_vars
}
call_delay = delay.duration.fold(node.arg_exprs)
except KeyError as e:
if getattr(node, "remote_fn", False):
note = diagnostic.Diagnostic("note",
"function called here", {},
node.loc)
self.abort("due to arguments passed remotely", node.loc, note)
node.arg_exprs = {
arg: self.evaluate(args[arg], abort=abort,
context="in the expression for argument '{}' "
"that affects I/O delay".format(arg))
for arg in free_vars
}
call_delay = delay.duration.fold(node.arg_exprs)
else:
assert False
else:

View File

@ -215,7 +215,7 @@ class LLVMIRGenerator:
typ = typ.find()
if types.is_tuple(typ):
return ll.LiteralStructType([self.llty_of_type(eltty) for eltty in typ.elts])
elif types.is_rpc(typ) or types.is_external_function(typ) or types.is_subkernel(typ):
elif types.is_rpc(typ) or types.is_external_function(typ):
if for_return:
return llvoid
else:
@ -398,15 +398,6 @@ class LLVMIRGenerator:
elif name == "rpc_recv":
llty = ll.FunctionType(lli32, [llptr])
elif name == "subkernel_send_message":
llty = ll.FunctionType(llvoid, [lli32, lli8, llsliceptr, llptrptr])
elif name == "subkernel_load_run":
llty = ll.FunctionType(llvoid, [lli32, lli1])
elif name == "subkernel_await_finish":
llty = ll.FunctionType(llvoid, [lli32, lli64])
elif name == "subkernel_await_message":
llty = ll.FunctionType(lli8, [lli32, lli64, llsliceptr, lli8, lli8])
# with now-pinning
elif name == "now":
llty = lli64
@ -883,53 +874,6 @@ class LLVMIRGenerator:
llvalue = self.llbuilder.bitcast(llvalue, llptr.type.pointee)
return self.llbuilder.store(llvalue, llptr)
def process_GetArgFromRemote(self, insn):
llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [],
name="subkernel.arg.stack")
llval = self._build_rpc_recv(insn.arg_type, llstackptr)
return llval
def process_GetOptArgFromRemote(self, insn):
# optarg = index < rcv_count ? Some(rcv_recv()) : None
llhead = self.llbuilder.basic_block
llrcv = self.llbuilder.append_basic_block(name="optarg.get.{}".format(insn.arg_name))
# argument received
self.llbuilder.position_at_end(llrcv)
llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [],
name="subkernel.arg.stack")
llval = self._build_rpc_recv(insn.arg_type, llstackptr)
llrpcretblock = self.llbuilder.basic_block # 'return' from rpc_recv, will be needed later
# create the tail block, needs to be after the rpc recv tail block
lltail = self.llbuilder.append_basic_block(name="optarg.tail.{}".format(insn.arg_name))
self.llbuilder.branch(lltail)
# go back to head to add a branch to the tail
self.llbuilder.position_at_end(llhead)
llargrcvd = self.llbuilder.icmp_unsigned("<", self.map(insn.index), self.map(insn.rcv_count))
self.llbuilder.cbranch(llargrcvd, llrcv, lltail)
# argument not received/after arg recvd
self.llbuilder.position_at_end(lltail)
llargtype = self.llty_of_type(insn.arg_type)
llphi_arg_present = self.llbuilder.phi(lli1, name="optarg.phi.present.{}".format(insn.arg_name))
llphi_arg = self.llbuilder.phi(llargtype, name="optarg.phi.{}".format(insn.arg_name))
llphi_arg_present.add_incoming(ll.Constant(lli1, 0), llhead)
llphi_arg.add_incoming(ll.Constant(llargtype, ll.Undefined), llhead)
llphi_arg_present.add_incoming(ll.Constant(lli1, 1), llrpcretblock)
llphi_arg.add_incoming(llval, llrpcretblock)
lloptarg = ll.Constant(ll.LiteralStructType([lli1, llargtype]), ll.Undefined)
lloptarg = self.llbuilder.insert_value(lloptarg, llphi_arg_present, 0)
lloptarg = self.llbuilder.insert_value(lloptarg, llphi_arg, 1)
return lloptarg
def attr_index(self, typ, attr):
return list(typ.attributes.keys()).index(attr)
@ -954,8 +898,8 @@ class LLVMIRGenerator:
def get_global_closure_ptr(self, typ, attr):
closure_type = typ.attributes[attr]
assert types.is_constructor(typ)
assert types.is_function(closure_type) or types.is_rpc(closure_type) or types.is_subkernel(closure_type)
if types.is_external_function(closure_type) or types.is_rpc(closure_type) or types.is_subkernel(closure_type):
assert types.is_function(closure_type) or types.is_rpc(closure_type)
if types.is_external_function(closure_type) or types.is_rpc(closure_type):
return None
llty = self.llty_of_type(typ.attributes[attr])
@ -1400,36 +1344,9 @@ class LLVMIRGenerator:
return self.llbuilder.call(self.llbuiltin("delay_mu"), [llinterval])
elif insn.op == "end_catch":
return self.llbuilder.call(self.llbuiltin("__artiq_end_catch"), [])
elif insn.op == "subkernel_await_finish":
llsid = self.map(insn.operands[0])
lltimeout = self.map(insn.operands[1])
return self.llbuilder.call(self.llbuiltin("subkernel_await_finish"), [llsid, lltimeout],
name="subkernel.await.finish")
elif insn.op == "subkernel_retrieve_return":
llsid = self.map(insn.operands[0])
lltimeout = self.map(insn.operands[1])
lltagptr = self._build_subkernel_tags([insn.type])
self.llbuilder.call(self.llbuiltin("subkernel_await_message"),
[llsid, lltimeout, lltagptr, ll.Constant(lli8, 1), ll.Constant(lli8, 1)],
name="subkernel.await.message")
llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [],
name="subkernel.arg.stack")
return self._build_rpc_recv(insn.type, llstackptr)
elif insn.op == "subkernel_preload":
llsid = self.map(insn.operands[0])
return self.llbuilder.call(self.llbuiltin("subkernel_load_run"), [llsid, ll.Constant(lli1, 0)],
name="subkernel.preload")
else:
assert False
def process_SubkernelAwaitArgs(self, insn):
llmin = self.map(insn.operands[0])
llmax = self.map(insn.operands[1])
lltagptr = self._build_subkernel_tags(insn.arg_types)
return self.llbuilder.call(self.llbuiltin("subkernel_await_message"),
[ll.Constant(lli32, 0), ll.Constant(lli64, 10_000), lltagptr, llmin, llmax],
name="subkernel.await.args")
def process_Closure(self, insn):
llenv = self.map(insn.environment())
llenv = self.llbuilder.bitcast(llenv, llptr)
@ -1509,76 +1426,6 @@ class LLVMIRGenerator:
return llfun, list(llargs), llarg_attrs, llcallstackptr
def _build_subkernel_tags(self, tag_list):
def ret_error_handler(typ):
printer = types.TypePrinter()
note = diagnostic.Diagnostic("note",
"value of type {type}",
{"type": printer.name(typ)},
fun_loc)
diag = diagnostic.Diagnostic("error",
"type {type} is not supported in subkernels",
{"type": printer.name(fun_type.ret)},
fun_loc, notes=[note])
self.engine.process(diag)
tag = b"".join([ir.rpc_tag(arg_type, ret_error_handler) for arg_type in tag_list])
lltag = self.llconst_of_const(ir.Constant(tag, builtins.TStr()))
lltagptr = self.llbuilder.alloca(lltag.type)
self.llbuilder.store(lltag, lltagptr)
return lltagptr
def _build_rpc_recv(self, ret, llstackptr, llnormalblock=None, llunwindblock=None):
# T result = {
# void *ret_ptr = alloca(sizeof(T));
# void *ptr = ret_ptr;
# loop: int size = rpc_recv(ptr);
# // Non-zero: Provide `size` bytes of extra storage for variable-length data.
# if(size) { ptr = alloca(size); goto loop; }
# else *(T*)ret_ptr
# }
llprehead = self.llbuilder.basic_block
llhead = self.llbuilder.append_basic_block(name="rpc.head")
if llunwindblock:
llheadu = self.llbuilder.append_basic_block(name="rpc.head.unwind")
llalloc = self.llbuilder.append_basic_block(name="rpc.continue")
lltail = self.llbuilder.append_basic_block(name="rpc.tail")
llretty = self.llty_of_type(ret)
llslot = self.llbuilder.alloca(llretty, name="rpc.ret.alloc")
llslotgen = self.llbuilder.bitcast(llslot, llptr, name="rpc.ret.ptr")
self.llbuilder.branch(llhead)
self.llbuilder.position_at_end(llhead)
llphi = self.llbuilder.phi(llslotgen.type, name="rpc.ptr")
llphi.add_incoming(llslotgen, llprehead)
if llunwindblock:
llsize = self.llbuilder.invoke(self.llbuiltin("rpc_recv"), [llphi],
llheadu, llunwindblock,
name="rpc.size.next")
self.llbuilder.position_at_end(llheadu)
else:
llsize = self.llbuilder.call(self.llbuiltin("rpc_recv"), [llphi],
name="rpc.size.next")
lldone = self.llbuilder.icmp_unsigned('==', llsize, ll.Constant(llsize.type, 0),
name="rpc.done")
self.llbuilder.cbranch(lldone, lltail, llalloc)
self.llbuilder.position_at_end(llalloc)
llalloca = self.llbuilder.alloca(lli8, llsize, name="rpc.alloc")
llalloca.align = self.max_target_alignment
llphi.add_incoming(llalloca, llalloc)
self.llbuilder.branch(llhead)
self.llbuilder.position_at_end(lltail)
llret = self.llbuilder.load(llslot, name="rpc.ret")
if not ret.fold(False, lambda r, t: r or builtins.is_allocated(t)):
# We didn't allocate anything except the slot for the value itself.
# Don't waste stack space.
self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr])
if llnormalblock:
self.llbuilder.branch(llnormalblock)
return llret
def _build_rpc(self, fun_loc, fun_type, args, llnormalblock, llunwindblock):
llservice = ll.Constant(lli32, fun_type.service)
@ -1654,102 +1501,56 @@ class LLVMIRGenerator:
return ll.Undefined
llret = self._build_rpc_recv(fun_type.ret, llstackptr, llnormalblock, llunwindblock)
# T result = {
# void *ret_ptr = alloca(sizeof(T));
# void *ptr = ret_ptr;
# loop: int size = rpc_recv(ptr);
# // Non-zero: Provide `size` bytes of extra storage for variable-length data.
# if(size) { ptr = alloca(size); goto loop; }
# else *(T*)ret_ptr
# }
llprehead = self.llbuilder.basic_block
llhead = self.llbuilder.append_basic_block(name="rpc.head")
if llunwindblock:
llheadu = self.llbuilder.append_basic_block(name="rpc.head.unwind")
llalloc = self.llbuilder.append_basic_block(name="rpc.continue")
lltail = self.llbuilder.append_basic_block(name="rpc.tail")
return llret
llretty = self.llty_of_type(fun_type.ret)
llslot = self.llbuilder.alloca(llretty, name="rpc.ret.alloc")
llslotgen = self.llbuilder.bitcast(llslot, llptr, name="rpc.ret.ptr")
self.llbuilder.branch(llhead)
def _build_subkernel_call(self, fun_loc, fun_type, args):
llsid = ll.Constant(lli32, fun_type.sid)
tag = b""
self.llbuilder.position_at_end(llhead)
llphi = self.llbuilder.phi(llslotgen.type, name="rpc.ptr")
llphi.add_incoming(llslotgen, llprehead)
if llunwindblock:
llsize = self.llbuilder.invoke(self.llbuiltin("rpc_recv"), [llphi],
llheadu, llunwindblock,
name="rpc.size.next")
self.llbuilder.position_at_end(llheadu)
else:
llsize = self.llbuilder.call(self.llbuiltin("rpc_recv"), [llphi],
name="rpc.size.next")
lldone = self.llbuilder.icmp_unsigned('==', llsize, ll.Constant(llsize.type, 0),
name="rpc.done")
self.llbuilder.cbranch(lldone, lltail, llalloc)
for arg in args:
def arg_error_handler(typ):
printer = types.TypePrinter()
note = diagnostic.Diagnostic("note",
"value of type {type}",
{"type": printer.name(typ)},
arg.loc)
diag = diagnostic.Diagnostic("error",
"type {type} is not supported in subkernel calls",
{"type": printer.name(arg.type)},
arg.loc, notes=[note])
self.engine.process(diag)
tag += ir.rpc_tag(arg.type, arg_error_handler)
tag += b":"
self.llbuilder.position_at_end(llalloc)
llalloca = self.llbuilder.alloca(lli8, llsize, name="rpc.alloc")
llalloca.align = self.max_target_alignment
llphi.add_incoming(llalloca, llalloc)
self.llbuilder.branch(llhead)
# run the kernel first
self.llbuilder.call(self.llbuiltin("subkernel_load_run"), [llsid, ll.Constant(lli1, 1)])
# arg sent in the same vein as RPC
llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [],
name="subkernel.stack")
lltag = self.llconst_of_const(ir.Constant(tag, builtins.TStr()))
lltagptr = self.llbuilder.alloca(lltag.type)
self.llbuilder.store(lltag, lltagptr)
if args:
# only send args if there's anything to send, 'self' is excluded
llargs = self.llbuilder.alloca(llptr, ll.Constant(lli32, len(args)),
name="subkernel.args")
for index, arg in enumerate(args):
if builtins.is_none(arg.type):
llargslot = self.llbuilder.alloca(llunit,
name="subkernel.arg{}".format(index))
else:
llarg = self.map(arg)
llargslot = self.llbuilder.alloca(llarg.type,
name="subkernel.arg{}".format(index))
self.llbuilder.store(llarg, llargslot)
llargslot = self.llbuilder.bitcast(llargslot, llptr)
llargptr = self.llbuilder.gep(llargs, [ll.Constant(lli32, index)])
self.llbuilder.store(llargslot, llargptr)
llargcount = ll.Constant(lli8, len(args))
self.llbuilder.call(self.llbuiltin("subkernel_send_message"),
[llsid, llargcount, lltagptr, llargs])
self.llbuilder.position_at_end(lltail)
llret = self.llbuilder.load(llslot, name="rpc.ret")
if not fun_type.ret.fold(False, lambda r, t: r or builtins.is_allocated(t)):
# We didn't allocate anything except the slot for the value itself.
# Don't waste stack space.
self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr])
return llsid
def _build_subkernel_return(self, insn):
# builds a remote return.
# unlike args, return only sends one thing.
if builtins.is_none(insn.value().type):
# do not waste time and bandwidth on Nones
return
def ret_error_handler(typ):
printer = types.TypePrinter()
note = diagnostic.Diagnostic("note",
"value of type {type}",
{"type": printer.name(typ)},
fun_loc)
diag = diagnostic.Diagnostic("error",
"return type {type} is not supported in subkernel returns",
{"type": printer.name(fun_type.ret)},
fun_loc, notes=[note])
self.engine.process(diag)
tag = ir.rpc_tag(insn.value().type, ret_error_handler)
tag += b":"
lltag = self.llconst_of_const(ir.Constant(tag, builtins.TStr()))
lltagptr = self.llbuilder.alloca(lltag.type)
self.llbuilder.store(lltag, lltagptr)
llrets = self.llbuilder.alloca(llptr, ll.Constant(lli32, 1),
name="subkernel.return")
llret = self.map(insn.value())
llretslot = self.llbuilder.alloca(llret.type, name="subkernel.retval")
self.llbuilder.store(llret, llretslot)
llretslot = self.llbuilder.bitcast(llretslot, llptr)
self.llbuilder.store(llretslot, llrets)
llsid = ll.Constant(lli32, 0) # return goes back to master, sid is ignored
lltagcount = ll.Constant(lli8, 1) # only one thing is returned
self.llbuilder.call(self.llbuiltin("subkernel_send_message"),
[llsid, lltagcount, lltagptr, llrets])
if llnormalblock:
self.llbuilder.branch(llnormalblock)
return llret
def process_Call(self, insn):
functiontyp = insn.target_function().type
@ -1758,10 +1559,6 @@ class LLVMIRGenerator:
functiontyp,
insn.arguments(),
llnormalblock=None, llunwindblock=None)
elif types.is_subkernel(functiontyp):
return self._build_subkernel_call(insn.target_function().loc,
functiontyp,
insn.arguments())
elif types.is_external_function(functiontyp):
llfun, llargs, llarg_attrs, llcallstackptr = self._prepare_ffi_call(insn)
else:
@ -1798,11 +1595,6 @@ class LLVMIRGenerator:
functiontyp,
insn.arguments(),
llnormalblock, llunwindblock)
elif types.is_subkernel(functiontyp):
return self._build_subkernel_call(insn.target_function().loc,
functiontyp,
insn.arguments(),
llnormalblock, llunwindblock)
elif types.is_external_function(functiontyp):
llfun, llargs, llarg_attrs, llcallstackptr = self._prepare_ffi_call(insn)
else:
@ -1881,8 +1673,7 @@ class LLVMIRGenerator:
attrvalue = getattr(value, attr)
is_class_function = (types.is_constructor(typ) and
types.is_function(typ.attributes[attr]) and
not types.is_external_function(typ.attributes[attr]) and
not types.is_subkernel(typ.attributes[attr]))
not types.is_external_function(typ.attributes[attr]))
if is_class_function:
attrvalue = self.embedding_map.specialize_function(typ.instance, attrvalue)
if not (types.is_instance(typ) and attr in typ.constant_attributes):
@ -1967,8 +1758,7 @@ class LLVMIRGenerator:
llelts = [self._quote(v, t, lambda: path() + [str(i)])
for i, (v, t) in enumerate(zip(value, typ.elts))]
return ll.Constant(llty, llelts)
elif types.is_rpc(typ) or types.is_external_function(typ) or \
types.is_builtin_function(typ) or types.is_subkernel(typ):
elif types.is_rpc(typ) or types.is_external_function(typ) or types.is_builtin_function(typ):
# RPC, C and builtin functions have no runtime representation.
return ll.Constant(llty, ll.Undefined)
elif types.is_function(typ):
@ -2023,8 +1813,6 @@ class LLVMIRGenerator:
return llinsn
def process_Return(self, insn):
if insn.remote_return:
self._build_subkernel_return(insn)
if builtins.is_none(insn.value().type):
return self.llbuilder.ret_void()
else:

View File

@ -385,50 +385,6 @@ class TRPC(Type):
def __hash__(self):
return hash(self.service)
class TSubkernel(TFunction):
"""
A kernel to be run on a satellite.
:ivar args: (:class:`collections.OrderedDict` of string to :class:`Type`)
function arguments
:ivar ret: (:class:`Type`)
return type
:ivar sid: (int) subkernel ID number
:ivar destination: (int) satellite destination number
"""
attributes = OrderedDict()
def __init__(self, args, optargs, ret, sid, destination):
assert isinstance(ret, Type)
super().__init__(args, optargs, ret)
self.sid, self.destination = sid, destination
self.delay = TFixedDelay(iodelay.Const(0))
def unify(self, other):
if other is self:
return
if isinstance(other, TSubkernel) and \
self.sid == other.sid and \
self.destination == other.destination:
self.ret.unify(other.ret)
elif isinstance(other, TVar):
other.unify(self)
else:
raise UnificationError(self, other)
def __repr__(self):
if getattr(builtins, "__in_sphinx__", False):
return str(self)
return "artiq.compiler.types.TSubkernel({})".format(repr(self.ret))
def __eq__(self, other):
return isinstance(other, TSubkernel) and \
self.sid == other.sid
def __hash__(self):
return hash(self.sid)
class TBuiltin(Type):
"""
An instance of builtin type. Every instance of a builtin
@ -688,9 +644,6 @@ def is_function(typ):
def is_rpc(typ):
return isinstance(typ.find(), TRPC)
def is_subkernel(typ):
return isinstance(typ.find(), TSubkernel)
def is_external_function(typ, name=None):
typ = typ.find()
if name is None:
@ -857,10 +810,6 @@ class TypePrinter(object):
return "[rpc{} #{}](...)->{}".format(typ.service,
" async" if typ.is_async else "",
self.name(typ.ret, depth + 1))
elif isinstance(typ, TSubkernel):
return "<subkernel{} dest#{}>->{}".format(typ.sid,
typ.destination,
self.name(typ.ret, depth + 1))
elif isinstance(typ, TBuiltinFunction):
return "<function {}>".format(typ.name)
elif isinstance(typ, (TConstructor, TExceptionConstructor)):

View File

@ -102,20 +102,8 @@ class RegionOf(algorithm.Visitor):
if types.is_external_function(node.func.type, "cache_get"):
# The cache is borrow checked dynamically
return Global()
if (types.is_builtin_function(node.func.type, "array")
or types.is_builtin_function(node.func.type, "make_array")
or types.is_builtin_function(node.func.type, "numpy.transpose")):
# While lifetime tracking across function calls in general is currently
# broken (see below), these special builtins that allocate an array on
# the stack of the caller _always_ allocate regardless of the parameters,
# and we can thus handle them without running into the precision issue
# mentioned in commit ae999db.
return self.visit_allocating(node)
# FIXME: Return statement missing here, but see m-labs/artiq#1497 and
# commit ae999db.
self.visit_sometimes_allocating(node)
else:
self.visit_sometimes_allocating(node)
# Value lives as long as the object/container, if it's mutable,
# or else forever

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
from artiq.language.core import kernel
class AD9154:
"""Kernel interface to AD9154 registers, using non-realtime SPI."""
def __init__(self, dmgr, spi_device, chip_select):
self.core = dmgr.get("core")
self.bus = dmgr.get(spi_device)
self.chip_select = chip_select
@kernel
def setup_bus(self, div=16):
self.bus.set_config_mu(0, 24, div, self.chip_select)
@kernel
def write(self, addr, data):
self.bus.write((addr << 16) | (data<< 8))
@kernel
def read(self, addr):
self.write((1 << 15) | addr, 0)
return self.bus.read()

View File

@ -25,14 +25,12 @@ class AD9912:
f_ref/clk_div*pll_n where f_ref is the reference frequency and clk_div
is the reference clock divider (both set in the parent Urukul CPLD
instance).
:param pll_en: PLL enable bit, set to 0 to bypass PLL (default: 1).
Note that when bypassing the PLL the red front panel LED may remain on.
"""
def __init__(self, dmgr, chip_select, cpld_device, sw_device=None,
pll_n=10, pll_en=1):
pll_n=10):
self.kernel_invariants = {"cpld", "core", "bus", "chip_select",
"pll_n", "pll_en", "ftw_per_hz"}
"pll_n", "ftw_per_hz"}
self.cpld = dmgr.get(cpld_device)
self.core = self.cpld.core
self.bus = self.cpld.bus
@ -41,12 +39,8 @@ class AD9912:
if sw_device:
self.sw = dmgr.get(sw_device)
self.kernel_invariants.add("sw")
self.pll_en = pll_en
self.pll_n = pll_n
if pll_en:
sysclk = self.cpld.refclk / [1, 1, 2, 4][self.cpld.clk_div] * pll_n
else:
sysclk = self.cpld.refclk
sysclk = self.cpld.refclk / [1, 1, 2, 4][self.cpld.clk_div] * pll_n
assert sysclk <= 1e9
self.ftw_per_hz = 1 / sysclk * (int64(1) << 48)
@ -108,15 +102,13 @@ class AD9912:
raise ValueError("Urukul AD9912 product id mismatch")
delay(50 * us)
# HSTL power down, CMOS power down
pwrcntrl1 = 0x80 | ((~self.pll_en & 1) << 4)
self.write(AD9912_PWRCNTRL1, pwrcntrl1, length=1)
self.write(AD9912_PWRCNTRL1, 0x80, length=1)
self.cpld.io_update.pulse(2 * us)
self.write(AD9912_N_DIV, self.pll_n // 2 - 2, length=1)
self.cpld.io_update.pulse(2 * us)
# I_cp = 375 µA, VCO high range
self.write(AD9912_PLLCFG, 0b00000101, length=1)
self.cpld.io_update.pulse(2 * us)
if self.pll_en:
self.write(AD9912_N_DIV, self.pll_n // 2 - 2, length=1)
self.cpld.io_update.pulse(2 * us)
# I_cp = 375 µA, VCO high range
self.write(AD9912_PLLCFG, 0b00000101, length=1)
self.cpld.io_update.pulse(2 * us)
delay(1 * ms)
@kernel

View File

@ -80,13 +80,6 @@ class AD9914:
self.set_x_duration_mu = 7 * self.write_duration_mu
self.exit_x_duration_mu = 3 * self.write_duration_mu
@staticmethod
def get_rtio_channels(bus_channel, channel, **kwargs):
# return only first entry, as there are several devices with the same RTIO channel
if channel == 0:
return [(bus_channel, None)]
return []
@kernel
def write(self, addr, data):
rtio_output((self.bus_channel << 8) | addr, data)

View File

@ -73,10 +73,6 @@ class ADF5356:
self._init_registers()
@staticmethod
def get_rtio_channels(**kwargs):
return []
@kernel
def init(self, blind=False):
"""

View File

@ -1,185 +0,0 @@
from artiq.language.core import kernel, portable
from numpy import int32
# almazny-specific data
ALMAZNY_LEGACY_REG_BASE = 0x0C
ALMAZNY_LEGACY_OE_SHIFT = 12
# higher SPI write divider to match almazny shift register timing
# min SER time before SRCLK rise = 125ns
# -> div=32 gives 125ns for data before clock rise
# works at faster dividers too but could be less reliable
ALMAZNY_LEGACY_SPIT_WR = 32
class AlmaznyLegacy:
"""
Almazny (High frequency mezzanine board for Mirny)
This applies to Almazny hardware v1.1 and earlier.
Use :class:`artiq.coredevice.almazny.AlmaznyChannel` for Almazny v1.2 and later.
:param host_mirny: Mirny device Almazny is connected to
"""
def __init__(self, dmgr, host_mirny):
self.mirny_cpld = dmgr.get(host_mirny)
self.att_mu = [0x3f] * 4
self.channel_sw = [0] * 4
self.output_enable = False
@kernel
def init(self):
self.output_toggle(self.output_enable)
@kernel
def att_to_mu(self, att):
"""
Convert an attenuator setting in dB to machine units.
:param att: attenuator setting in dB [0-31.5]
:return: attenuator setting in machine units
"""
mu = round(att * 2.0)
if mu > 63 or mu < 0:
raise ValueError("Invalid Almazny attenuator settings!")
return mu
@kernel
def mu_to_att(self, att_mu):
"""
Convert a digital attenuator setting to dB.
:param att_mu: attenuator setting in machine units
:return: attenuator setting in dB
"""
return att_mu / 2
@kernel
def set_att(self, channel, att, rf_switch=True):
"""
Sets attenuators on chosen shift register (channel).
:param channel: index of the register [0-3]
:param att: attenuation setting in dBm [0-31.5]
:param rf_switch: rf switch (bool)
"""
self.set_att_mu(channel, self.att_to_mu(att), rf_switch)
@kernel
def set_att_mu(self, channel, att_mu, rf_switch=True):
"""
Sets attenuators on chosen shift register (channel).
:param channel: index of the register [0-3]
:param att_mu: attenuation setting in machine units [0-63]
:param rf_switch: rf switch (bool)
"""
self.channel_sw[channel] = 1 if rf_switch else 0
self.att_mu[channel] = att_mu
self._update_register(channel)
@kernel
def output_toggle(self, oe):
"""
Toggles output on all shift registers on or off.
:param oe: toggle output enable (bool)
"""
self.output_enable = oe
cfg_reg = self.mirny_cpld.read_reg(1)
en = 1 if self.output_enable else 0
delay(100 * us)
new_reg = (en << ALMAZNY_LEGACY_OE_SHIFT) | (cfg_reg & 0x3FF)
self.mirny_cpld.write_reg(1, new_reg)
delay(100 * us)
@kernel
def _flip_mu_bits(self, mu):
# in this form MSB is actually 0.5dB attenuator
# unnatural for users, so we flip the six bits
return (((mu & 0x01) << 5)
| ((mu & 0x02) << 3)
| ((mu & 0x04) << 1)
| ((mu & 0x08) >> 1)
| ((mu & 0x10) >> 3)
| ((mu & 0x20) >> 5))
@kernel
def _update_register(self, ch):
self.mirny_cpld.write_ext(
ALMAZNY_LEGACY_REG_BASE + ch,
8,
self._flip_mu_bits(self.att_mu[ch]) | (self.channel_sw[ch] << 6),
ALMAZNY_LEGACY_SPIT_WR
)
delay(100 * us)
@kernel
def _update_all_registers(self):
for i in range(4):
self._update_register(i)
class AlmaznyChannel:
"""
One Almazny channel
Almazny is a mezzanine for the Quad PLL RF source Mirny that exposes and
controls the frequency-doubled outputs.
This driver requires Almazny hardware revision v1.2 or later
and Mirny CPLD gateware v0.3 or later.
Use :class:`artiq.coredevice.almazny.AlmaznyLegacy` for Almazny hardware v1.1 and earlier.
:param host_mirny: Mirny CPLD device name
:param channel: channel index (0-3)
"""
def __init__(self, dmgr, host_mirny, channel):
self.channel = channel
self.mirny_cpld = dmgr.get(host_mirny)
@portable
def to_mu(self, att, enable, led):
"""
Convert an attenuation in dB, RF switch state and LED state to machine
units.
:param att: attenuator setting in dB (0-31.5)
:param enable: RF switch state (bool)
:param led: LED state (bool)
:return: channel setting in machine units
"""
mu = int32(round(att * 2.))
if mu >= 64 or mu < 0:
raise ValueError("Attenuation out of range")
# unfortunate hardware design: bit reverse
mu = ((mu & 0x15) << 1) | ((mu >> 1) & 0x15)
mu = ((mu & 0x03) << 4) | (mu & 0x0c) | ((mu >> 4) & 0x03)
if enable:
mu |= 1 << 6
if led:
mu |= 1 << 7
return mu
@kernel
def set_mu(self, mu):
"""
Set channel state (machine units).
:param mu: channel state in machine units.
"""
self.mirny_cpld.write_ext(
addr=0xc + self.channel, length=8, data=mu, ext_div=32)
@kernel
def set(self, att, enable, led=False):
"""
Set attenuation, RF switch, and LED state (SI units).
:param att: attenuator setting in dB (0-31.5)
:param enable: RF switch state (bool)
:param led: LED state (bool)
"""
self.set_mu(self.to_mu(att, enable, led))

View File

@ -0,0 +1,79 @@
from artiq.language.core import kernel, portable, delay
from artiq.language.units import us, ms
from artiq.coredevice.shiftreg import ShiftReg
@portable
def to_mu(att):
return round(att*2.0) ^ 0x3f
@portable
def from_mu(att_mu):
return 0.5*(att_mu ^ 0x3f)
class BaseModAtt:
def __init__(self, dmgr, rst_n, clk, le, mosi, miso):
self.rst_n = dmgr.get(rst_n)
self.shift_reg = ShiftReg(dmgr,
clk=clk, ser=mosi, latch=le, ser_in=miso, n=8*4)
@kernel
def reset(self):
# HMC's incompetence in digital design and interfaces means that
# the HMC542 needs a level low on RST_N and then a rising edge
# on Latch Enable. Their "latch" isn't a latch but a DFF.
# Of course, it also powers up with a random attenuation, and
# that cannot be fixed with simple pull-ups/pull-downs.
self.rst_n.off()
self.shift_reg.latch.off()
delay(1*us)
self.shift_reg.latch.on()
delay(1*us)
self.shift_reg.latch.off()
self.rst_n.on()
delay(1*us)
@kernel
def set_mu(self, att0, att1, att2, att3):
"""
Sets the four attenuators on BaseMod.
The values are in half decibels, between 0 (no attenuation)
and 63 (31.5dB attenuation).
"""
word = (
(att0 << 2) |
(att1 << 10) |
(att2 << 18) |
(att3 << 26)
)
self.shift_reg.set(word)
@kernel
def get_mu(self):
"""
Retrieves the current settings of the four attenuators on BaseMod.
"""
word = self.shift_reg.get()
att0 = (word >> 2) & 0x3f
att1 = (word >> 10) & 0x3f
att2 = (word >> 18) & 0x3f
att3 = (word >> 26) & 0x3f
return att0, att1, att2, att3
@kernel
def set(self, att0, att1, att2, att3):
"""
Sets the four attenuators on BaseMod.
The values are in decibels.
"""
self.set_mu(to_mu(att0), to_mu(att1), to_mu(att2), to_mu(att3))
@kernel
def get(self):
"""
Retrieves the current settings of the four attenuators on BaseMod.
The values are in decibels.
"""
att0, att1, att2, att3 = self.get_mu()
return from_mu(att0), from_mu(att1), from_mu(att2), from_mu(att3)

View File

@ -23,8 +23,6 @@ class Request(Enum):
RPCReply = 7
RPCException = 8
SubkernelUpload = 9
class Reply(Enum):
SystemInfo = 2
@ -210,7 +208,6 @@ class CommKernel:
self.unpack_float64 = struct.Struct(self.endian + "d").unpack
self.pack_header = struct.Struct(self.endian + "lB").pack
self.pack_int8 = struct.Struct(self.endian + "B").pack
self.pack_int32 = struct.Struct(self.endian + "l").pack
self.pack_int64 = struct.Struct(self.endian + "q").pack
self.pack_float64 = struct.Struct(self.endian + "d").pack
@ -325,7 +322,7 @@ class CommKernel:
self._write(chunk)
def _write_int8(self, value):
self._write(self.pack_int8(value))
self._write(value)
def _write_int32(self, value):
self._write(self.pack_int32(value))
@ -385,19 +382,6 @@ class CommKernel:
else:
self._read_expect(Reply.LoadCompleted)
def upload_subkernel(self, kernel_library, id, destination):
self._write_header(Request.SubkernelUpload)
self._write_int32(id)
self._write_int8(destination)
self._write_bytes(kernel_library)
self._flush()
self._read_header()
if self._read_type == Reply.LoadFailed:
raise LoadError(self._read_string())
else:
self._read_expect(Reply.LoadCompleted)
def run(self):
self._write_empty(Request.RunKernel)
self._flush()

View File

@ -1,6 +1,5 @@
import os, sys
import numpy
from inspect import getfullargspec
from functools import wraps
from pythonparser import diagnostic
@ -54,17 +53,6 @@ def rtio_get_counter() -> TInt64:
raise NotImplementedError("syscall not simulated")
def get_target_cls(target):
if target == "rv32g":
return RV32GTarget
elif target == "rv32ima":
return RV32IMATarget
elif target == "cortexa9":
return CortexA9Target
else:
raise ValueError("Unsupported target")
class Core:
"""Core device driver.
@ -78,70 +66,58 @@ class Core:
:param ref_multiplier: ratio between the RTIO fine timestamp frequency
and the RTIO coarse timestamp frequency (e.g. SERDES multiplication
factor).
:param analyzer_proxy: name of the core device analyzer proxy to trigger
(optional).
:param analyze_at_run_end: automatically trigger the core device analyzer
proxy after the Experiment's run stage finishes.
"""
kernel_invariants = {
"core", "ref_period", "coarse_ref_period", "ref_multiplier",
}
def __init__(self, dmgr,
host, ref_period,
analyzer_proxy=None, analyze_at_run_end=False,
ref_multiplier=8,
target="rv32g", satellite_cpu_targets={}):
def __init__(self, dmgr, host, ref_period, ref_multiplier=8, target="rv32g"):
self.ref_period = ref_period
self.ref_multiplier = ref_multiplier
self.satellite_cpu_targets = satellite_cpu_targets
self.target_cls = get_target_cls(target)
if target == "rv32g":
self.target_cls = RV32GTarget
elif target == "rv32ima":
self.target_cls = RV32IMATarget
elif target == "cortexa9":
self.target_cls = CortexA9Target
else:
raise ValueError("Unsupported target")
self.coarse_ref_period = ref_period*ref_multiplier
if host is None:
self.comm = CommKernelDummy()
else:
self.comm = CommKernel(host)
self.analyzer_proxy_name = analyzer_proxy
self.analyze_at_run_end = analyze_at_run_end
self.first_run = True
self.dmgr = dmgr
self.core = self
self.comm.core = self
self.analyzer_proxy = None
def notify_run_end(self):
if self.analyze_at_run_end:
self.trigger_analyzer_proxy()
def close(self):
self.comm.close()
def compile(self, function, args, kwargs, set_result=None,
attribute_writeback=True, print_as_rpc=True,
target=None, destination=0, subkernel_arg_types=[]):
attribute_writeback=True, print_as_rpc=True):
try:
engine = _DiagnosticEngine(all_errors_are_fatal=True)
stitcher = Stitcher(engine=engine, core=self, dmgr=self.dmgr,
print_as_rpc=print_as_rpc,
destination=destination, subkernel_arg_types=subkernel_arg_types)
print_as_rpc=print_as_rpc)
stitcher.stitch_call(function, args, kwargs, set_result)
stitcher.finalize()
module = Module(stitcher,
ref_period=self.ref_period,
attribute_writeback=attribute_writeback)
target = target if target is not None else self.target_cls()
target = self.target_cls()
library = target.compile_and_link([module])
stripped_library = target.strip(library)
return stitcher.embedding_map, stripped_library, \
lambda addresses: target.symbolize(library, addresses), \
lambda symbols: target.demangle(symbols), \
module.subkernel_arg_types
lambda symbols: target.demangle(symbols)
except diagnostic.Error as error:
raise CompileError(error.diagnostic) from error
@ -159,38 +135,11 @@ class Core:
def set_result(new_result):
nonlocal result
result = new_result
embedding_map, kernel_library, symbolizer, demangler, subkernel_arg_types = \
embedding_map, kernel_library, symbolizer, demangler = \
self.compile(function, args, kwargs, set_result)
self.compile_and_upload_subkernels(embedding_map, args, subkernel_arg_types)
self._run_compiled(kernel_library, embedding_map, symbolizer, demangler)
return result
def compile_subkernel(self, sid, subkernel_fn, embedding_map, args, subkernel_arg_types):
# pass self to subkernels (if applicable)
# assuming the first argument is self
subkernel_args = getfullargspec(subkernel_fn.artiq_embedded.function)
self_arg = []
if len(subkernel_args[0]) > 0:
if subkernel_args[0][0] == 'self':
self_arg = args[:1]
destination = subkernel_fn.artiq_embedded.destination
destination_tgt = self.satellite_cpu_targets[destination]
target = get_target_cls(destination_tgt)(subkernel_id=sid)
object_map, kernel_library, _, _, _ = \
self.compile(subkernel_fn, self_arg, {}, attribute_writeback=False,
print_as_rpc=False, target=target, destination=destination,
subkernel_arg_types=subkernel_arg_types.get(sid, []))
if object_map.has_rpc_or_subkernel():
raise ValueError("Subkernel must not use RPC or subkernels in other destinations")
return destination, kernel_library
def compile_and_upload_subkernels(self, embedding_map, args, subkernel_arg_types):
for sid, subkernel_fn in embedding_map.subkernels().items():
destination, kernel_library = \
self.compile_subkernel(sid, subkernel_fn, embedding_map,
args, subkernel_arg_types)
self.comm.upload_subkernel(kernel_library, sid, destination)
def precompile(self, function, *args, **kwargs):
"""Precompile a kernel and return a callable that executes it on the core device
at a later time.
@ -199,7 +148,7 @@ class Core:
as additional positional and keyword arguments.
The returned callable accepts no arguments.
Precompiled kernels may use RPCs and subkernels.
Precompiled kernels may use RPCs.
Object attributes at the beginning of a precompiled kernel execution have the
values they had at precompilation time. If up-to-date values are required,
@ -224,9 +173,8 @@ class Core:
nonlocal result
result = new_result
embedding_map, kernel_library, symbolizer, demangler, subkernel_arg_types = \
embedding_map, kernel_library, symbolizer, demangler = \
self.compile(function, args, kwargs, set_result, attribute_writeback=False)
self.compile_and_upload_subkernels(embedding_map, args, subkernel_arg_types)
@wraps(function)
def run_precompiled():
@ -302,23 +250,3 @@ class Core:
min_now = rtio_get_counter() + 125000
if now_mu() < min_now:
at_mu(min_now)
def trigger_analyzer_proxy(self):
"""Causes the core analyzer proxy to retrieve a dump from the device,
and distribute it to all connected clients (typically dashboards).
Returns only after the dump has been retrieved from the device.
Raises IOError if no analyzer proxy has been configured, or if the
analyzer proxy fails. In the latter case, more details would be
available in the proxy log.
"""
if self.analyzer_proxy is None:
if self.analyzer_proxy_name is not None:
self.analyzer_proxy = self.dmgr.get(self.analyzer_proxy_name)
if self.analyzer_proxy is None:
raise IOError("No analyzer proxy configured")
else:
success = self.analyzer_proxy.trigger()
if not success:
raise IOError("Analyzer proxy reported failure")

View File

@ -19,24 +19,16 @@
},
"min_artiq_version": {
"type": "string",
"description": "Minimum required ARTIQ version",
"default": "0"
"description": "Minimum required ARTIQ version"
},
"hw_rev": {
"type": "string",
"description": "Hardware revision"
},
"base": {
"type": "string",
"enum": ["use_drtio_role", "standalone", "master", "satellite"],
"description": "Deprecated, use drtio_role instead",
"default": "use_drtio_role"
},
"drtio_role": {
"type": "string",
"enum": ["standalone", "master", "satellite"],
"description": "Role that this device takes in a DRTIO network; 'standalone' means no DRTIO",
"default": "standalone"
"description": "SoC base; value depends on intended system topology"
},
"ext_ref_frequency": {
"type": "number",
@ -142,7 +134,7 @@
"properties": {
"type": {
"type": "string",
"enum": ["dio", "dio_spi", "urukul", "novogorny", "sampler", "suservo", "zotino", "grabber", "mirny", "fastino", "phaser", "hvamp", "shuttler"]
"enum": ["dio", "dio_spi", "urukul", "novogorny", "sampler", "suservo", "zotino", "grabber", "mirny", "fastino", "phaser", "hvamp"]
},
"board": {
"type": "string"
@ -212,8 +204,7 @@
"type": "object",
"properties": {
"name": {
"type": "string",
"default": "dio_spi"
"type": "string"
},
"clk": {
"type": "integer",
@ -249,8 +240,7 @@
"type": "object",
"properties": {
"name": {
"type": "string",
"default": "ttl"
"type": "string"
},
"pin": {
"type": "integer",
@ -267,8 +257,7 @@
}
},
"required": ["pin", "direction"]
},
"default": []
}
}
},
"required": ["ports", "spi"]
@ -308,18 +297,11 @@
"clk_div": {
"type": "integer",
"minimum": 0,
"maximum": 3,
"default": 0
"maximum": 3
},
"pll_n": {
"type": "integer"
},
"pll_en": {
"type": "integer",
"minimum": 0,
"maximum": 1,
"default": 1
},
"pll_vco": {
"type": "integer"
},
@ -394,11 +376,6 @@
"minItems": 2,
"maxItems": 2
},
"sampler_hw_rev": {
"type": "string",
"pattern": "^v[0-9]+\\.[0-9]+",
"default": "v2.2"
},
"urukul0_ports": {
"type": "array",
"items": {
@ -428,12 +405,6 @@
"type": "integer",
"default": 32
},
"pll_en": {
"type": "integer",
"minimum": 0,
"maximum": 1,
"default": 1
},
"pll_vco": {
"type": "integer"
}
@ -525,11 +496,6 @@
"almazny": {
"type": "boolean",
"default": false
},
"almazny_hw_rev": {
"type": "string",
"pattern": "^v[0-9]+\\.[0-9]+",
"default": "v1.2"
}
},
"required": ["ports"]
@ -579,11 +545,6 @@
},
"minItems": 1,
"maxItems": 1
},
"mode": {
"type": "string",
"enum": ["base", "miqro"],
"default": "base"
}
},
"required": ["ports"]
@ -610,31 +571,6 @@
},
"required": ["ports"]
}
},{
"title": "Shuttler",
"if": {
"properties": {
"type": {
"const": "shuttler"
}
}
},
"then": {
"properties": {
"ports": {
"type": "array",
"items": {
"type": "integer"
},
"minItems": 1,
"maxItems": 2
},
"drtio_destination": {
"type": "integer"
}
},
"required": ["ports"]
}
}]
}
}

View File

@ -6,7 +6,7 @@ alone could achieve.
"""
from artiq.language.core import syscall, kernel
from artiq.language.types import TInt32, TInt64, TStr, TNone, TTuple, TBool
from artiq.language.types import TInt32, TInt64, TStr, TNone, TTuple
from artiq.coredevice.exceptions import DMAError
from numpy import int64
@ -17,7 +17,7 @@ def dma_record_start(name: TStr) -> TNone:
raise NotImplementedError("syscall not simulated")
@syscall
def dma_record_stop(duration: TInt64, enable_ddma: TBool) -> TNone:
def dma_record_stop(duration: TInt64) -> TNone:
raise NotImplementedError("syscall not simulated")
@syscall
@ -25,11 +25,11 @@ def dma_erase(name: TStr) -> TNone:
raise NotImplementedError("syscall not simulated")
@syscall
def dma_retrieve(name: TStr) -> TTuple([TInt64, TInt32, TBool]):
def dma_retrieve(name: TStr) -> TTuple([TInt64, TInt32]):
raise NotImplementedError("syscall not simulated")
@syscall
def dma_playback(timestamp: TInt64, ptr: TInt32, enable_ddma: TBool) -> TNone:
def dma_playback(timestamp: TInt64, ptr: TInt32) -> TNone:
raise NotImplementedError("syscall not simulated")
@ -47,7 +47,6 @@ class DMARecordContextManager:
def __init__(self):
self.name = ""
self.saved_now_mu = int64(0)
self.enable_ddma = False
@kernel
def __enter__(self):
@ -57,7 +56,7 @@ class DMARecordContextManager:
@kernel
def __exit__(self, type, value, traceback):
dma_record_stop(now_mu(), self.enable_ddma) # see above
dma_record_stop(now_mu()) # see above
at_mu(self.saved_now_mu)
@ -75,20 +74,12 @@ class CoreDMA:
self.epoch = 0
@kernel
def record(self, name, enable_ddma=False):
def record(self, name):
"""Returns a context manager that will record a DMA trace called ``name``.
Any previously recorded trace with the same name is overwritten.
The trace will persist across kernel switches.
In DRTIO context, distributed DMA can be toggled with ``enable_ddma``.
Enabling it allows running DMA on satellites, rather than sending all
events from the master.
Keeping it disabled it may improve performance in some scenarios,
e.g. when there are many small satellite buffers."""
The trace will persist across kernel switches."""
self.epoch += 1
self.recorder.name = name
self.recorder.enable_ddma = enable_ddma
return self.recorder
@kernel
@ -101,24 +92,24 @@ class CoreDMA:
def playback(self, name):
"""Replays a previously recorded DMA trace. This function blocks until
the entire trace is submitted to the RTIO FIFOs."""
(advance_mu, ptr, uses_ddma) = dma_retrieve(name)
dma_playback(now_mu(), ptr, uses_ddma)
(advance_mu, ptr) = dma_retrieve(name)
dma_playback(now_mu(), ptr)
delay_mu(advance_mu)
@kernel
def get_handle(self, name):
"""Returns a handle to a previously recorded DMA trace. The returned handle
is only valid until the next call to :meth:`record` or :meth:`erase`."""
(advance_mu, ptr, uses_ddma) = dma_retrieve(name)
return (self.epoch, advance_mu, ptr, uses_ddma)
(advance_mu, ptr) = dma_retrieve(name)
return (self.epoch, advance_mu, ptr)
@kernel
def playback_handle(self, handle):
"""Replays a handle obtained with :meth:`get_handle`. Using this function
is much faster than :meth:`playback` for replaying a set of traces repeatedly,
but incurs the overhead of managing the handles onto the programmer."""
(epoch, advance_mu, ptr, uses_ddma) = handle
(epoch, advance_mu, ptr) = handle
if self.epoch != epoch:
raise DMAError("Invalid handle")
dma_playback(now_mu(), ptr, uses_ddma)
dma_playback(now_mu(), ptr)
delay_mu(advance_mu)

View File

@ -91,10 +91,6 @@ class EdgeCounter:
self.channel = channel
self.counter_max = (1 << (gateware_width - 1)) - 1
@staticmethod
def get_rtio_channels(channel, **kwargs):
return [(channel, None)]
@kernel
def gate_rising(self, duration):
"""Count rising edges for the given duration and request the total at

View File

@ -148,13 +148,6 @@ class DMAError(Exception):
artiq_builtin = True
class SubkernelError(Exception):
"""Raised when an operation regarding a subkernel is invalid
or cannot be completed.
"""
artiq_builtin = True
class ClockFailure(Exception):
"""Raised when RTIO PLL has lost lock."""

View File

@ -21,7 +21,7 @@ class Fastino:
DAC updates synchronized to a frame edge.
The `log2_width=0` RTIO layout uses one DAC channel per RTIO address and a
dense RTIO address space. The RTIO words are narrow (32 bit) and
dense RTIO address space. The RTIO words are narrow. (32 bit) and
few-channel updates are efficient. There is the least amount of DAC state
tracking in kernels, at the cost of more DMA and RTIO data.
The setting here and in the RTIO PHY (gateware) must match.
@ -52,10 +52,6 @@ class Fastino:
assert self.core.ref_period == 1*ns
self.t_frame = int64(14*7*4)
@staticmethod
def get_rtio_channels(channel, **kwargs):
return [(channel, None)]
@kernel
def init(self):
"""Initialize the device.

View File

@ -25,10 +25,6 @@ class Grabber:
# ROI engine outputs for one video frame.
self.sentinel = int32(int64(2**count_width))
@staticmethod
def get_rtio_channels(channel_base, **kwargs):
return [(channel_base, "ROI coordinates"), (channel_base + 1, "ROI mask")]
@kernel
def setup_roi(self, n, x0, y0, x1, y1):
"""

View File

@ -32,7 +32,4 @@ def load(description_path):
global validator
validator.validate(result)
if result["base"] != "use_drtio_role":
result["drtio_role"] = result["base"]
return result

View File

@ -31,6 +31,16 @@ WE = 1 << 24
# supported CPLD code version
PROTO_REV_MATCH = 0x0
# almazny-specific data
ALMAZNY_REG_BASE = 0x0C
ALMAZNY_OE_SHIFT = 12
# higher SPI write divider to match almazny shift register timing
# min SER time before SRCLK rise = 125ns
# -> div=32 gives 125ns for data before clock rise
# works at faster dividers too but could be less reliable
ALMAZNY_SPIT_WR = 32
class Mirny:
"""
@ -167,3 +177,109 @@ class Mirny:
if length < 32:
data <<= 32 - length
self.bus.write(data)
class Almazny:
"""
Almazny (High frequency mezzanine board for Mirny)
:param host_mirny: Mirny device Almazny is connected to
"""
def __init__(self, dmgr, host_mirny):
self.mirny_cpld = dmgr.get(host_mirny)
self.att_mu = [0x3f] * 4
self.channel_sw = [0] * 4
self.output_enable = False
@kernel
def init(self):
self.output_toggle(self.output_enable)
@kernel
def att_to_mu(self, att):
"""
Convert an attenuator setting in dB to machine units.
:param att: attenuator setting in dB [0-31.5]
:return: attenuator setting in machine units
"""
mu = round(att * 2.0)
if mu > 63 or mu < 0:
raise ValueError("Invalid Almazny attenuator settings!")
return mu
@kernel
def mu_to_att(self, att_mu):
"""
Convert a digital attenuator setting to dB.
:param att_mu: attenuator setting in machine units
:return: attenuator setting in dB
"""
return att_mu / 2
@kernel
def set_att(self, channel, att, rf_switch=True):
"""
Sets attenuators on chosen shift register (channel).
:param channel: index of the register [0-3]
:param att: attenuation setting in dBm [0-31.5]
:param rf_switch: rf switch (bool)
"""
self.set_att_mu(channel, self.att_to_mu(att), rf_switch)
@kernel
def set_att_mu(self, channel, att_mu, rf_switch=True):
"""
Sets attenuators on chosen shift register (channel).
:param channel: index of the register [0-3]
:param att_mu: attenuation setting in machine units [0-63]
:param rf_switch: rf switch (bool)
"""
self.channel_sw[channel] = 1 if rf_switch else 0
self.att_mu[channel] = att_mu
self._update_register(channel)
@kernel
def output_toggle(self, oe):
"""
Toggles output on all shift registers on or off.
:param oe: toggle output enable (bool)
"""
self.output_enable = oe
cfg_reg = self.mirny_cpld.read_reg(1)
en = 1 if self.output_enable else 0
delay(100 * us)
new_reg = (en << ALMAZNY_OE_SHIFT) | (cfg_reg & 0x3FF)
self.mirny_cpld.write_reg(1, new_reg)
delay(100 * us)
@kernel
def _flip_mu_bits(self, mu):
# in this form MSB is actually 0.5dB attenuator
# unnatural for users, so we flip the six bits
return (((mu & 0x01) << 5)
| ((mu & 0x02) << 3)
| ((mu & 0x04) << 1)
| ((mu & 0x08) >> 1)
| ((mu & 0x10) >> 3)
| ((mu & 0x20) >> 5))
@kernel
def _update_register(self, ch):
self.mirny_cpld.write_ext(
ALMAZNY_REG_BASE + ch,
8,
self._flip_mu_bits(self.att_mu[ch]) | (self.channel_sw[ch] << 6),
ALMAZNY_SPIT_WR
)
delay(100 * us)
@kernel
def _update_all_registers(self):
for i in range(4):
self._update_register(i)

View File

@ -9,10 +9,6 @@ from artiq.coredevice.trf372017 import TRF372017
PHASER_BOARD_ID = 19
PHASER_GW_BASE = 1
PHASER_GW_MIQRO = 2
PHASER_ADDR_BOARD_ID = 0x00
PHASER_ADDR_HW_REV = 0x01
PHASER_ADDR_GW_REV = 0x02
@ -44,20 +40,6 @@ PHASER_ADDR_DUC1_P = 0x26
PHASER_ADDR_DAC1_DATA = 0x28
PHASER_ADDR_DAC1_TEST = 0x2c
# servo registers
PHASER_ADDR_SERVO_CFG0 = 0x30
PHASER_ADDR_SERVO_CFG1 = 0x31
# 0x32 - 0x71 servo coefficients + offset data
PHASER_ADDR_SERVO_DATA_BASE = 0x32
# 0x72 - 0x78 Miqro channel profile/window memories
PHASER_ADDR_MIQRO_MEM_ADDR = 0x72
PHASER_ADDR_MIQRO_MEM_DATA = 0x74
# Miqro profile memory select
PHASER_MIQRO_SEL_PROFILE = 1 << 14
PHASER_SEL_DAC = 1 << 0
PHASER_SEL_TRF0 = 1 << 1
PHASER_SEL_TRF1 = 1 << 2
@ -76,11 +58,6 @@ PHASER_DAC_SEL_TEST = 1
PHASER_HW_REV_VARIANT = 1 << 4
SERVO_COEFF_WIDTH = 16
SERVO_DATA_WIDTH = 16
SERVO_COEFF_SHIFT = 14
SERVO_T_CYCLE = (32+12+192+24+4)*ns # Must match gateware ADC parameters
class Phaser:
"""Phaser 4-channel, 16-bit, 1 GS/s DAC coredevice driver.
@ -88,26 +65,6 @@ class Phaser:
Phaser contains a 4 channel, 1 GS/s DAC chip with integrated upconversion,
quadrature modulation compensation and interpolation features.
The coredevice RTIO PHY and the Phaser gateware come in different modes
that have different features. Phaser mode and coredevice PHY mode are both
selected at their respective gateware compile-time and need to match.
=============== ============== ===================================
Phaser gateware Coredevice PHY Features per :class:`PhaserChannel`
=============== ============== ===================================
Base <= v0.5 Base Base (5 :class:`PhaserOscillator`)
Base >= v0.6 Base Base + Servo
Miqro >= v0.6 Miqro :class:`Miqro`
=============== ============== ===================================
The coredevice driver (this class and :class:`PhaserChannel`) exposes
the superset of all functionality regardless of the Coredevice RTIO PHY
or Phaser gateware modes. This is to evade type unification limitations.
Features absent in Coredevice PHY/Phaser gateware will not work and
should not be accessed.
**Base mode**
The coredevice produces 2 IQ (in-phase and quadrature) data streams with 25
MS/s and 14 bit per quadrature. Each data stream supports 5 independent
numerically controlled IQ oscillators (NCOs, DDSs with 32 bit frequency, 16
@ -138,14 +95,6 @@ class Phaser:
absolute phase with respect to other RTIO input and output events
(see `get_next_frame_mu()`).
**Miqro mode**
See :class:`Miqro`
Here the DAC operates in 4x interpolation.
**Analog flow**
The four analog DAC outputs are passed through anti-aliasing filters.
In the baseband variant, the even/in-phase DAC channels feed 31.5 dB range
@ -163,33 +112,6 @@ class Phaser:
configured through a shared SPI bus that is accessed and controlled via
FPGA registers.
**Servo**
Each phaser output channel features a servo to control the RF output amplitude
using feedback from an ADC. The servo consists of a first order IIR (infinite
impulse response) filter fed by the ADC and a multiplier that scales the I
and Q datastreams from the DUC by the IIR output. The IIR state is updated at
the 3.788 MHz ADC sampling rate.
Each channel IIR features 4 profiles, each consisting of the [b0, b1, a1] filter
coefficients as well as an output offset. The coefficients and offset can be
set for each profile individually and the profiles each have their own ``y0``,
``y1`` output registers (the ``x0``, ``x1`` inputs are shared). To avoid
transient effects, care should be taken to not update the coefficents in the
currently selected profile.
The servo can be en- or disabled for each channel. When disabled, the servo
output multiplier is simply bypassed and the datastream reaches the DAC unscaled.
The IIR output can be put on hold for each channel. In hold mode, the filter
still ingests samples and updates its input ``x0`` and ``x1`` registers, but
does not update the ``y0``, ``y1`` output registers.
After power-up the servo is disabled, in profile 0, with coefficients [0, 0, 0]
and hold is enabled. If older gateware without ther servo is loaded onto the
Phaser FPGA, the device simply behaves as if the servo is disabled and none of
the servo functions have any effect.
.. note:: Various register settings of the DAC and the quadrature
upconverters are available to be modified through the `dac`, `trf0`,
`trf1` dictionaries. These can be set through the device database
@ -229,7 +151,7 @@ class Phaser:
"dac_mmap"}
def __init__(self, dmgr, channel_base, miso_delay=1, tune_fifo_offset=True,
clk_sel=0, sync_dly=0, dac=None, trf0=None, trf1=None, gw_rev=PHASER_GW_BASE,
clk_sel=0, sync_dly=0, dac=None, trf0=None, trf1=None,
core_device="core"):
self.channel_base = channel_base
self.core = dmgr.get(core_device)
@ -243,25 +165,12 @@ class Phaser:
self.clk_sel = clk_sel
self.tune_fifo_offset = tune_fifo_offset
self.sync_dly = sync_dly
self.gw_rev = gw_rev # verified in init()
self.dac_mmap = DAC34H84(dac).get_mmap()
self.channel = [PhaserChannel(self, ch, trf)
for ch, trf in enumerate([trf0, trf1])]
@staticmethod
def get_rtio_channels(channel_base, gw_rev=PHASER_GW_BASE, **kwargs):
if gw_rev == PHASER_GW_MIQRO:
return [(channel_base, "base"), (channel_base + 1, "ch0"), (channel_base + 2, "ch1")]
elif gw_rev == PHASER_GW_BASE:
return [(channel_base, "base"),
(channel_base + 1, "ch0 frequency"),
(channel_base + 2, "ch0 phase amplitude"),
(channel_base + 3, "ch1 frequency"),
(channel_base + 4, "ch1 phase amplitude")]
raise ValueError("invalid gw_rev `{}`".format(gw_rev))
@kernel
def init(self, debug=False):
"""Initialize the board.
@ -281,9 +190,8 @@ class Phaser:
gw_rev = self.read8(PHASER_ADDR_GW_REV)
if debug:
print("gw_rev:", self.gw_rev)
print("gw_rev:", gw_rev)
self.core.break_realtime()
assert gw_rev == self.gw_rev
delay(.1*ms) # slack
# allow a few errors during startup and alignment since boot
@ -400,40 +308,34 @@ class Phaser:
if channel.get_att_mu() != 0x5a:
raise ValueError("attenuator test failed")
delay(.1*ms)
channel.set_att_mu(0x00) # maximum attenuation
channel.set_att_mu(0x00) # minimum attenuation
channel.set_servo(profile=0, enable=0, hold=1)
if self.gw_rev == PHASER_GW_BASE:
# test oscillators and DUC
for i in range(len(channel.oscillator)):
oscillator = channel.oscillator[i]
asf = 0
if i == 0:
asf = 0x7fff
# 6pi/4 phase
oscillator.set_amplitude_phase_mu(asf=asf, pow=0xc000, clr=1)
delay(1*us)
# 3pi/4
channel.set_duc_phase_mu(0x6000)
channel.set_duc_cfg(select=0, clr=1)
self.duc_stb()
delay(.1*ms) # settle link, pipeline and impulse response
data = channel.get_dac_data()
# test oscillators and DUC
for i in range(len(channel.oscillator)):
oscillator = channel.oscillator[i]
asf = 0
if i == 0:
asf = 0x7fff
# 6pi/4 phase
oscillator.set_amplitude_phase_mu(asf=asf, pow=0xc000, clr=1)
delay(1*us)
channel.oscillator[0].set_amplitude_phase_mu(asf=0, pow=0xc000,
clr=1)
delay(.1*ms)
sqrt2 = 0x5a81 # 0x7fff/sqrt(2)
data_i = data & 0xffff
data_q = (data >> 16) & 0xffff
# allow ripple
if (data_i < sqrt2 - 30 or data_i > sqrt2 or
abs(data_i - data_q) > 2):
raise ValueError("DUC+oscillator phase/amplitude test failed")
if self.gw_rev == PHASER_GW_MIQRO:
channel.miqro.reset()
# 3pi/4
channel.set_duc_phase_mu(0x6000)
channel.set_duc_cfg(select=0, clr=1)
self.duc_stb()
delay(.1*ms) # settle link, pipeline and impulse response
data = channel.get_dac_data()
delay(1*us)
channel.oscillator[0].set_amplitude_phase_mu(asf=0, pow=0xc000,
clr=1)
delay(.1*ms)
sqrt2 = 0x5a81 # 0x7fff/sqrt(2)
data_i = data & 0xffff
data_q = (data >> 16) & 0xffff
# allow ripple
if (data_i < sqrt2 - 30 or data_i > sqrt2 or
abs(data_i - data_q) > 2):
raise ValueError("DUC+oscillator phase/amplitude test failed")
if is_baseband:
continue
@ -480,12 +382,6 @@ class Phaser:
response = rtio_input_data(self.channel_base)
return response >> self.miso_delay
@kernel
def write16(self, addr, data: TInt32):
"""Write 16 bit to a sequence of FPGA registers."""
self.write8(addr, data >> 8)
self.write8(addr + 1, data)
@kernel
def write32(self, addr, data: TInt32):
"""Write 32 bit to a sequence of FPGA registers."""
@ -840,8 +736,6 @@ class Phaser:
if good & (1 << o):
sum += o
count += 1
if count == 0:
raise ValueError("no good fifo offset")
best = ((sum // count) + offset) % 8
self.dac_write(0x09, (config9 & 0x1fff) | (best << 13))
return best
@ -852,9 +746,8 @@ class PhaserChannel:
A Phaser channel contains:
* multiple :class:`PhaserOscillator` (in the coredevice phy),
* multiple oscillators (in the coredevice phy),
* an interpolation chain and digital upconverter (DUC) on Phaser,
* a :class:`Miqro` instance on Phaser,
* several channel-specific settings in the DAC:
* quadrature modulation compensation QMC
@ -866,7 +759,6 @@ class PhaserChannel:
Attributes:
* :attr:`oscillator`: List of five :class:`PhaserOscillator`.
* :attr:`miqro`: A :class:`Miqro`.
.. note:: The amplitude sum of the oscillators must be less than one to
avoid clipping or overflow. If any of the DDS or DUC frequencies are
@ -879,8 +771,6 @@ class PhaserChannel:
changes in oscillator parameters, the overshoot can lead to clipping
or overflow after the interpolation. Either band-limit any changes
in the oscillator parameters or back off the amplitude sufficiently.
Miqro is not affected by this. But both the oscillators and Miqro can
be affected by intrinsic overshoot of the interpolator on the DAC.
"""
kernel_invariants = {"index", "phaser", "trf_mmap"}
@ -890,7 +780,6 @@ class PhaserChannel:
self.trf_mmap = TRF372017(trf).get_mmap()
self.oscillator = [PhaserOscillator(self, osc) for osc in range(5)]
self.miqro = Miqro(self)
@kernel
def get_dac_data(self) -> TInt32:
@ -1150,133 +1039,6 @@ class PhaserChannel:
data = data ^ ((1 << 12) | (1 << 13))
self.trf_write(data)
@kernel
def set_servo(self, profile=0, enable=0, hold=0):
"""Set the servo configuration.
:param enable: 1 to enable servo, 0 to disable servo (default). If disabled,
the servo is bypassed and hold is enforced since the control loop is broken.
:param hold: 1 to hold the servo IIR filter output constant, 0 for normal operation.
:param profile: Profile index to select for channel. (0 to 3)
"""
if (profile < 0) or (profile > 3):
raise ValueError("invalid profile index")
addr = PHASER_ADDR_SERVO_CFG0 + self.index
# enforce hold if the servo is disabled
data = (profile << 2) | (((hold | ~enable) & 1) << 1) | (enable & 1)
self.phaser.write8(addr, data)
@kernel
def set_iir_mu(self, profile, b0, b1, a1, offset):
"""Load a servo profile consiting of the three filter coefficients and an output offset.
Avoid setting the IIR parameters of the currently active profile.
The recurrence relation is (all data signed and MSB aligned):
.. math::
a_0 y_n = a_1 y_{n - 1} + b_0 x_n + b_1 x_{n - 1} + o
Where:
* :math:`y_n` and :math:`y_{n-1}` are the current and previous
filter outputs, clipped to :math:`[0, 1[`.
* :math:`x_n` and :math:`x_{n-1}` are the current and previous
filter inputs in :math:`[-1, 1[`.
* :math:`o` is the offset
* :math:`a_0` is the normalization factor :math:`2^{14}`
* :math:`a_1` is the feedback gain
* :math:`b_0` and :math:`b_1` are the feedforward gains for the two
delays
.. seealso:: :meth:`set_iir`
:param profile: Profile to set (0 to 3)
:param b0: b0 filter coefficient (16 bit signed)
:param b1: b1 filter coefficient (16 bit signed)
:param a1: a1 filter coefficient (16 bit signed)
:param offset: Output offset (16 bit signed)
"""
if (profile < 0) or (profile > 3):
raise ValueError("invalid profile index")
# 32 byte-sized data registers per channel and 8 (2 bytes * (3 coefficients + 1 offset)) registers per profile
addr = PHASER_ADDR_SERVO_DATA_BASE + (8 * profile) + (self.index * 32)
for data in [b0, b1, a1, offset]:
self.phaser.write16(addr, data)
addr += 2
@kernel
def set_iir(self, profile, kp, ki=0., g=0., x_offset=0., y_offset=0.):
"""Set servo profile IIR coefficients.
Avoid setting the IIR parameters of the currently active profile.
Gains are given in units of output full per scale per input full scale.
.. note:: Due to inherent constraints of the fixed point datatypes and IIR
filters, the ``x_offset`` (setpoint) resolution depends on the selected
gains. Low ``ki`` gains will lead to a low ``x_offset`` resolution.
The transfer function is (up to time discretization and
coefficient quantization errors):
.. math::
H(s) = k_p + \\frac{k_i}{s + \\frac{k_i}{g}}
Where:
* :math:`s = \\sigma + i\\omega` is the complex frequency
* :math:`k_p` is the proportional gain
* :math:`k_i` is the integrator gain
* :math:`g` is the integrator gain limit
:param profile: Profile number (0-3)
:param kp: Proportional gain. This is usually negative (closed
loop, positive ADC voltage, positive setpoint). When 0, this
implements a pure I controller.
:param ki: Integrator gain (rad/s). Equivalent to the gain at 1 Hz.
When 0 (the default) this implements a pure P controller.
Same sign as ``kp``.
:param g: Integrator gain limit (1). When 0 (the default) the
integrator gain limit is infinite. Same sign as ``ki``.
:param x_offset: IIR input offset. Used as the negative
setpoint when stabilizing to a desired input setpoint. Will
be converted to an equivalent output offset and added to y_offset.
:param y_offset: IIR output offset.
"""
NORM = 1 << SERVO_COEFF_SHIFT
COEFF_MAX = 1 << SERVO_COEFF_WIDTH - 1
DATA_MAX = 1 << SERVO_DATA_WIDTH - 1
kp *= NORM
if ki == 0.:
# pure P
a1 = 0
b1 = 0
b0 = int(round(kp))
else:
# I or PI
ki *= NORM*SERVO_T_CYCLE/2.
if g == 0.:
c = 1.
a1 = NORM
else:
c = 1./(1. + ki/(g*NORM))
a1 = int(round((2.*c - 1.)*NORM))
b0 = int(round(kp + ki*c))
b1 = int(round(kp + (ki - 2.*kp)*c))
if b1 == -b0:
raise ValueError("low integrator gain and/or gain limit")
if (b0 >= COEFF_MAX or b0 < -COEFF_MAX or
b1 >= COEFF_MAX or b1 < -COEFF_MAX):
raise ValueError("high gains")
forward_gain = (b0 + b1) * (1 << SERVO_DATA_WIDTH - 1 - SERVO_COEFF_SHIFT)
effective_offset = int(round(DATA_MAX * y_offset + forward_gain * x_offset))
self.set_iir_mu(profile, b0, b1, a1, effective_offset)
class PhaserOscillator:
"""Phaser IQ channel oscillator (NCO/DDS).
@ -1334,305 +1096,3 @@ class PhaserOscillator:
raise ValueError("amplitude out of bounds")
pow = int32(round(phase*(1 << 16)))
self.set_amplitude_phase_mu(asf, pow, clr)
class Miqro:
"""
Miqro pulse generator.
A Miqro instance represents one RF output. The DSP components are fully
contained in the Phaser gateware. The output is generated by with
the following data flow:
**Oscillators**
* There are n_osc = 16 oscillators with oscillator IDs 0..n_osc-1.
* Each oscillator outputs one tone at any given time
* I/Q (quadrature, a.k.a. complex) 2x16 bit signed data
at tau = 4 ns sample intervals, 250 MS/s, Nyquist 125 MHz, bandwidth 200 MHz
(from f = -100..+100 MHz, taking into account the interpolation anti-aliasing
filters in subsequent interpolators),
* 32 bit frequency (f) resolution (~ 1/16 Hz),
* 16 bit unsigned amplitude (a) resolution
* 16 bit phase offset (p) resolution
* The output phase p' of each oscillator at time t (boot/reset/initialization of the
device at t=0) is then p' = f*t + p (mod 1 turn) where f and p are the (currently
active) profile frequency and phase offset.
* Note: The terms "phase coherent" and "phase tracking" are defined to refer to this
choice of oscillator output phase p'. Note that the phase offset p is not relative to
(on top of previous phase/profiles/oscillator history).
It is "absolute" in the sense that frequency f and phase offset p fully determine
oscillator output phase p' at time t. This is unlike typical DDS behavior.
* Frequency, phase, and amplitude of each oscillator are configurable by selecting one of
n_profile = 32 profiles 0..n_profile-1. This selection is fast and can be done for
each pulse. The phase coherence defined above is guaranteed for each
profile individually.
* Note: one profile per oscillator (usually profile index 0) should be reserved
for the NOP (no operation, identity) profile, usually with zero amplitude.
* Data for each profile for each oscillator can be configured
individually. Storing profile data should be considered "expensive".
* Note: The annotation that some operation is "expensive" does not mean it is
impossible, just that it may take a significant amount of time and
resources to execute such that it may be impractical when used often or
during fast pulse sequences. They are intended for use in calibration and
initialization.
**Summation**
* The oscillator outputs are added together (wrapping addition).
* The user must ensure that the sum of oscillators outputs does not exceed the
data range. In general that means that the sum of the amplitudes must not
exceed one.
**Shaper**
* The summed complex output stream is then multiplied with a the complex-valued
output of a triggerable shaper.
* Triggering the shaper corresponds to passing a pulse from all oscillators to
the RF output.
* Selected profiles become active simultaneously (on the same output sample) when
triggering the shaper with the first shaper output sample.
* The shaper reads (replays) window samples from a memory of size n_window = 1 << 10.
* The window memory can be segmented by choosing different start indices
to support different windows.
* Each window memory segment starts with a header determining segment
length and interpolation parameters.
* The window samples are interpolated by a factor (rate change) between 1 and
r = 1 << 12.
* The interpolation order is constant, linear, quadratic, or cubic. This
corresponds to interpolation modes from rectangular window (1st order CIC)
or zero order hold) to Parzen window (4th order CIC or cubic spline).
* This results in support for single shot pulse lengths (envelope support) between
tau and a bit more than r * n_window * tau = (1 << 12 + 10) tau ~ 17 ms.
* Windows can be configured to be head-less and/or tail-less, meaning, they
do not feed zero-amplitude samples into the shaper before and after
each window respectively. This is used to implement pulses with arbitrary
length or CW output.
**Overall properties**
* The DAC may upconvert the signal by applying a frequency offset f1 with
phase p1.
* In the Upconverter Phaser variant, the analog quadrature upconverter
applies another frequency of f2 and phase p2.
* The resulting phase of the signal from one oscillator at the SMA output is
(f + f1 + f2)*t + p + s(t - t0) + p1 + p2 (mod 1 turn)
where s(t - t0) is the phase of the interpolated
shaper output, and t0 is the trigger time (fiducial of the shaper).
Unsurprisingly the frequency is the derivative of the phase.
* Group delays between pulse parameter updates are matched across oscillators,
shapers, and channels.
* The minimum time to change profiles and phase offsets is ~128 ns (estimate, TBC).
This is the minimum pulse interval.
The sustained pulse rate of the RTIO PHY/Fastlink is one pulse per Fastlink frame
(may be increased, TBC).
"""
def __init__(self, channel):
self.channel = channel
self.base_addr = (self.channel.phaser.channel_base + 1 +
self.channel.index) << 8
@kernel
def reset(self):
"""Establish no-output profiles and no-output window and execute them.
This establishes the first profile (index 0) on all oscillators as zero
amplitude, creates a trivial window (one sample with zero amplitude,
minimal interpolation), and executes a corresponding pulse.
"""
for osc in range(16):
self.set_profile_mu(osc, profile=0, ftw=0, asf=0)
delay(20*us)
self.set_window_mu(start=0, iq=[0], order=0)
self.pulse(window=0, profiles=[0])
@kernel
def set_profile_mu(self, oscillator, profile, ftw, asf, pow_=0):
"""Store an oscillator profile (machine units).
:param oscillator: Oscillator index (0 to 15)
:param profile: Profile index (0 to 31)
:param ftw: Frequency tuning word (32 bit signed integer on a 250 MHz clock)
:param asf: Amplitude scale factor (16 bit unsigned integer)
:param pow_: Phase offset word (16 bit integer)
"""
if oscillator >= 16:
raise ValueError("invalid oscillator index")
if profile >= 32:
raise ValueError("invalid profile index")
self.channel.phaser.write16(PHASER_ADDR_MIQRO_MEM_ADDR,
(self.channel.index << 15) | PHASER_MIQRO_SEL_PROFILE |
(oscillator << 6) | (profile << 1))
self.channel.phaser.write32(PHASER_ADDR_MIQRO_MEM_DATA, ftw)
self.channel.phaser.write32(PHASER_ADDR_MIQRO_MEM_DATA,
(asf & 0xffff) | (pow_ << 16))
@kernel
def set_profile(self, oscillator, profile, frequency, amplitude, phase=0.):
"""Store an oscillator profile.
:param oscillator: Oscillator index (0 to 15)
:param profile: Profile index (0 to 31)
:param frequency: Frequency in Hz (passband -100 to 100 MHz).
Interpreted in the Nyquist sense, i.e. aliased.
:param amplitude: Amplitude in units of full scale (0. to 1.)
:param phase: Phase in turns. See :class:`Miqro` for a definition of
phase in this context.
:return: The quantized 32 bit frequency tuning word
"""
ftw = int32(round(frequency*((1 << 30)/(62.5*MHz))))
asf = int32(round(amplitude*0xffff))
if asf < 0 or asf > 0xffff:
raise ValueError("amplitude out of bounds")
pow_ = int32(round(phase*(1 << 16)))
self.set_profile_mu(oscillator, profile, ftw, asf, pow_)
return ftw
@kernel
def set_window_mu(self, start, iq, rate=1, shift=0, order=3, head=1, tail=1):
"""Store a window segment (machine units)
:param start: Window start address (0 to 0x3ff)
:param iq: List of IQ window samples. Each window sample is an integer
containing the signed I part in the 16 LSB and the signed Q part in
the 16 MSB. The maximum window length is 0x3fe. The user must
ensure that this window does not overlap with other windows in the
memory.
:param rate: Interpolation rate change (1 to 1 << 12)
:param shift: Interpolator amplitude gain compensation in powers of 2 (0 to 63)
:param order: Interpolation order from 0 (corresponding to
constant/rectangular window/zero-order-hold/1st order CIC interpolation)
to 3 (corresponding to cubic/Parzen window/4th order CIC interpolation)
:param head: Update the interpolator settings and clear its state at the start
of the window. This also implies starting the envelope from zero.
:param tail: Feed zeros into the interpolator after the window samples.
In the absence of further pulses this will return the output envelope
to zero with the chosen interpolation.
:return: Next available window memory address after this segment.
"""
if start >= 1 << 10:
raise ValueError("start out of bounds")
if len(iq) >= 1 << 10:
raise ValueError("window length out of bounds")
if rate < 1 or rate > 1 << 12:
raise ValueError("rate out of bounds")
if shift > 0x3f:
raise ValueError("shift out of bounds")
if order > 3:
raise ValueError("order out of bounds")
self.channel.phaser.write16(PHASER_ADDR_MIQRO_MEM_ADDR,
(self.channel.index << 15) | start)
self.channel.phaser.write32(PHASER_ADDR_MIQRO_MEM_DATA,
(len(iq) & 0x3ff) |
((rate - 1) << 10) |
(shift << 22) |
(order << 28) |
((head & 1) << 30) |
((tail & 1) << 31)
)
for iqi in iq:
self.channel.phaser.write32(PHASER_ADDR_MIQRO_MEM_DATA, iqi)
delay(20*us) # slack for long windows
return (start + 1 + len(iq)) & 0x3ff
@kernel
def set_window(self, start, iq, period=4*ns, order=3, head=1, tail=1):
"""Store a window segment
:param start: Window start address (0 to 0x3ff)
:param iq: List of IQ window samples. Each window sample is a pair of
two float numbers -1 to 1, one for each I and Q in units of full scale.
The maximum window length is 0x3fe. The user must ensure that this window
does not overlap with other windows in the memory.
:param period: Desired window sample period in SI units (4*ns to (4 << 12)*ns).
:param order: Interpolation order from 0 (corresponding to
constant/zero-order-hold/1st order CIC interpolation) to 3 (corresponding
to cubic/Parzen/4th order CIC interpolation)
:param head: Update the interpolator settings and clear its state at the start
of the window. This also implies starting the envelope from zero.
:param tail: Feed zeros into the interpolator after the window samples.
In the absence of further pulses this will return the output envelope
to zero with the chosen interpolation.
:return: Actual sample period in SI units
"""
rate = int32(round(period/(4*ns)))
gain = 1.
for _ in range(order):
gain *= rate
shift = 0
while gain >= 2.:
shift += 1
gain *= .5
scale = ((1 << 15) - 1)/gain
iq_mu = [
(int32(round(iqi[0]*scale)) & 0xffff) |
(int32(round(iqi[1]*scale)) << 16)
for iqi in iq
]
self.set_window_mu(start, iq_mu, rate, shift, order, head, tail)
return (len(iq) + order)*rate*4*ns
@kernel
def encode(self, window, profiles, data):
"""Encode window and profile selection
:param window: Window start address (0 to 0x3ff)
:param profiles: List of profile indices for the oscillators. Maximum
length 16. Unused oscillators will be set to profile 0.
:param data: List of integers to store the encoded data words into.
Unused entries will remain untouched. Must contain at least three
lements if all oscillators are used and should be initialized to
zeros.
:return: Number of words from `data` used.
"""
if len(profiles) > 16:
raise ValueError("too many oscillators")
if window > 0x3ff:
raise ValueError("window start out of bounds")
data[0] = window
word = 0
idx = 10
for profile in profiles:
if profile > 0x1f:
raise ValueError("profile out of bounds")
if idx > 32 - 5:
word += 1
idx = 0
data[word] |= profile << idx
idx += 5
return word + 1
@kernel
def pulse_mu(self, data):
"""Emit a pulse (encoded)
The pulse fiducial timing resolution is 4 ns.
:param data: List of up to 3 words containing an encoded MIQRO pulse as
returned by :meth:`encode`.
"""
word = len(data)
delay_mu(-8*word) # back shift to align
while word > 0:
word -= 1
delay_mu(8)
# final write sets pulse stb
rtio_output(self.base_addr + word, data[word])
@kernel
def pulse(self, window, profiles):
"""Emit a pulse
This encodes the window and profiles (see :meth:`encode`) and emits them
(see :meth:`pulse_mu`).
:param window: Window start address (0 to 0x3ff)
:param profiles: List of profile indices for the oscillators. Maximum
length 16. Unused oscillators will select profile 0.
"""
data = [0, 0, 0]
words = self.encode(window, profiles, data)
self.pulse_mu(data[:words])

View File

@ -15,26 +15,24 @@ SPI_CS_PGIA = 1 # separate SPI bus, CS used as RCLK
@portable
def adc_mu_to_volt(data, gain=0, corrected_fs=True):
def adc_mu_to_volt(data, gain=0):
"""Convert ADC data in machine units to Volts.
:param data: 16 bit signed ADC word
:param gain: PGIA gain setting (0: 1, ..., 3: 1000)
:param corrected_fs: use corrected ADC FS reference.
Should be True for Samplers' revisions after v2.1. False for v2.1 and earlier.
:return: Voltage in Volts
"""
if gain == 0:
volt_per_lsb = 20.48 / (1 << 16) if corrected_fs else 20. / (1 << 16)
volt_per_lsb = 20./(1 << 16)
elif gain == 1:
volt_per_lsb = 2.048 / (1 << 16) if corrected_fs else 2. / (1 << 16)
volt_per_lsb = 2./(1 << 16)
elif gain == 2:
volt_per_lsb = .2048 / (1 << 16) if corrected_fs else .2 / (1 << 16)
volt_per_lsb = .2/(1 << 16)
elif gain == 3:
volt_per_lsb = 0.02048 / (1 << 16) if corrected_fs else .02 / (1 << 16)
volt_per_lsb = .02/(1 << 16)
else:
raise ValueError("invalid gain")
return data * volt_per_lsb
return data*volt_per_lsb
class Sampler:
@ -50,13 +48,12 @@ class Sampler:
:param gains: Initial value for PGIA gains shift register
(default: 0x0000). Knowledge of this state is not transferred
between experiments.
:param hw_rev: Sampler's hardware revision string (default 'v2.2')
:param core_device: Core device name
"""
kernel_invariants = {"bus_adc", "bus_pgia", "core", "cnv", "div", "corrected_fs"}
kernel_invariants = {"bus_adc", "bus_pgia", "core", "cnv", "div"}
def __init__(self, dmgr, spi_adc_device, spi_pgia_device, cnv_device,
div=8, gains=0x0000, hw_rev="v2.2", core_device="core"):
div=8, gains=0x0000, core_device="core"):
self.bus_adc = dmgr.get(spi_adc_device)
self.bus_adc.update_xfer_duration_mu(div, 32)
self.bus_pgia = dmgr.get(spi_pgia_device)
@ -65,11 +62,6 @@ class Sampler:
self.cnv = dmgr.get(cnv_device)
self.div = div
self.gains = gains
self.corrected_fs = self.use_corrected_fs(hw_rev)
@staticmethod
def use_corrected_fs(hw_rev):
return hw_rev != "v2.1"
@kernel
def init(self):
@ -152,4 +144,4 @@ class Sampler:
for i in range(n):
channel = i + 8 - len(data)
gain = (self.gains >> (channel*2)) & 0b11
data[i] = adc_mu_to_volt(adc_data[i], gain, self.corrected_fs)
data[i] = adc_mu_to_volt(adc_data[i], gain)

372
artiq/coredevice/sawg.py Normal file
View File

@ -0,0 +1,372 @@
"""
Driver for the Smart Arbitrary Waveform Generator (SAWG) on RTIO.
The SAWG is an "improved DDS" built in gateware and interfacing to
high-speed DACs.
Output event replacement is supported except on the configuration channel.
"""
from artiq.language.types import TInt32, TFloat
from numpy import int32, int64
from artiq.language.core import kernel
from artiq.coredevice.spline import Spline
from artiq.coredevice.rtio import rtio_output
# sawg.Config addresses
_SAWG_DIV = 0
_SAWG_CLR = 1
_SAWG_IQ_EN = 2
# _SAWF_PAD = 3 # reserved
_SAWG_OUT_MIN = 4
_SAWG_OUT_MAX = 5
_SAWG_DUC_MIN = 6
_SAWG_DUC_MAX = 7
class Config:
"""SAWG configuration.
Exposes the configurable quantities of a single SAWG channel.
Access to the configuration registers for a SAWG channel can not
be concurrent. There must be at least :attr:`_rtio_interval` machine
units of delay between accesses. Replacement is not supported and will be
lead to an ``RTIOCollision`` as this is likely a programming error.
All methods therefore advance the timeline by the duration of one
configuration register transfer.
:param channel: RTIO channel number of the channel.
:param core: Core device.
"""
kernel_invariants = {"channel", "core", "_out_scale", "_duc_scale",
"_rtio_interval"}
def __init__(self, channel, core, cordic_gain=1.):
self.channel = channel
self.core = core
# normalized DAC output
self._out_scale = (1 << 15) - 1.
# normalized DAC output including DUC cordic gain
self._duc_scale = self._out_scale/cordic_gain
# configuration channel access interval
self._rtio_interval = int64(3*self.core.ref_multiplier)
@kernel
def set_div(self, div: TInt32, n: TInt32=0):
"""Set the spline evolution divider and current counter value.
The divider and the spline evolution are synchronized across all
spline channels within a SAWG channel. The DDS/DUC phase accumulators
always evolves at full speed.
.. note:: The spline evolution divider has not been tested extensively
and is currently considered a technological preview only.
:param div: Spline evolution divider, such that
``t_sawg_spline/t_rtio_coarse = div + 1``. Default: ``0``.
:param n: Current value of the counter. Default: ``0``.
"""
rtio_output((self.channel << 8) | _SAWG_DIV, div | (n << 16))
delay_mu(self._rtio_interval)
@kernel
def set_clr(self, clr0: TInt32, clr1: TInt32, clr2: TInt32):
"""Set the accumulator clear mode for the three phase accumulators.
When the ``clr`` bit for a given DDS/DUC phase accumulator is
set, that phase accumulator will be cleared with every phase offset
RTIO command and the output phase of the DDS/DUC will be
exactly the phase RTIO value ("absolute phase update mode").
.. math::
q^\prime(t) = p^\prime + (t - t^\prime) f^\prime
In turn, when the bit is cleared, the phase RTIO channels
determine a phase offset to the current (carrier-) value of the
DDS/DUC phase accumulator. This "relative phase update mode" is
sometimes also called continuous phase mode.
.. math::
q^\prime(t) = q(t^\prime) + (p^\prime - p) +
(t - t^\prime) f^\prime
Where:
* :math:`q`, :math:`q^\prime`: old/new phase accumulator
* :math:`p`, :math:`p^\prime`: old/new phase offset
* :math:`f^\prime`: new frequency
* :math:`t^\prime`: timestamp of setting new :math:`p`, :math:`f`
* :math:`t`: running time
:param clr0: Auto-clear phase accumulator of the ``phase0``/
``frequency0`` DUC. Default: ``True``
:param clr1: Auto-clear phase accumulator of the ``phase1``/
``frequency1`` DDS. Default: ``True``
:param clr2: Auto-clear phase accumulator of the ``phase2``/
``frequency2`` DDS. Default: ``True``
"""
rtio_output((self.channel << 8) | _SAWG_CLR, clr0 |
(clr1 << 1) | (clr2 << 2))
delay_mu(self._rtio_interval)
@kernel
def set_iq_en(self, i_enable: TInt32, q_enable: TInt32):
"""Enable I/Q data on this DAC channel.
Every pair of SAWG channels forms a buddy pair.
The ``iq_en`` configuration controls which DDS data is emitted to the
DACs.
Refer to the documentation of :class:`SAWG` for a mathematical
description of ``i_enable`` and ``q_enable``.
.. note:: Quadrature data from the buddy channel is currently
a technological preview only. The data is ignored in the SAWG
gateware and not added to the DAC output.
This is equivalent to the ``q_enable`` switch always being ``0``.
:param i_enable: Controls adding the in-phase
DUC-DDS data of *this* SAWG channel to *this* DAC channel.
Default: ``1``.
:param q_enable: controls adding the quadrature
DUC-DDS data of this SAWG's *buddy* channel to *this* DAC
channel. Default: ``0``.
"""
rtio_output((self.channel << 8) | _SAWG_IQ_EN, i_enable |
(q_enable << 1))
delay_mu(self._rtio_interval)
@kernel
def set_duc_max_mu(self, limit: TInt32):
"""Set the digital up-converter (DUC) I and Q data summing junctions
upper limit. In machine units.
The default limits are chosen to reach maximum and minimum DAC output
amplitude.
For a description of the limiter functions in normalized units see:
.. seealso:: :meth:`set_duc_max`
"""
rtio_output((self.channel << 8) | _SAWG_DUC_MAX, limit)
delay_mu(self._rtio_interval)
@kernel
def set_duc_min_mu(self, limit: TInt32):
""".. seealso:: :meth:`set_duc_max_mu`"""
rtio_output((self.channel << 8) | _SAWG_DUC_MIN, limit)
delay_mu(self._rtio_interval)
@kernel
def set_out_max_mu(self, limit: TInt32):
""".. seealso:: :meth:`set_duc_max_mu`"""
rtio_output((self.channel << 8) | _SAWG_OUT_MAX, limit)
delay_mu(self._rtio_interval)
@kernel
def set_out_min_mu(self, limit: TInt32):
""".. seealso:: :meth:`set_duc_max_mu`"""
rtio_output((self.channel << 8) | _SAWG_OUT_MIN, limit)
delay_mu(self._rtio_interval)
@kernel
def set_duc_max(self, limit: TFloat):
"""Set the digital up-converter (DUC) I and Q data summing junctions
upper limit.
Each of the three summing junctions has a saturating adder with
configurable upper and lower limits. The three summing junctions are:
* At the in-phase input to the ``phase0``/``frequency0`` fast DUC,
after the anti-aliasing FIR filter.
* At the quadrature input to the ``phase0``/``frequency0``
fast DUC, after the anti-aliasing FIR filter. The in-phase and
quadrature data paths both use the same limits.
* Before the DAC, where the following three data streams
are added together:
* the output of the ``offset`` spline,
* (optionally, depending on ``i_enable``) the in-phase output
of the ``phase0``/``frequency0`` fast DUC, and
* (optionally, depending on ``q_enable``) the quadrature
output of the ``phase0``/``frequency0`` fast DUC of the
buddy channel.
Refer to the documentation of :class:`SAWG` for a mathematical
description of the summing junctions.
:param limit: Limit value ``[-1, 1]``. The output of the limiter will
never exceed this limit. The default limits are the full range
``[-1, 1]``.
.. seealso::
* :meth:`set_duc_max`: Upper limit of the in-phase and quadrature
inputs to the DUC.
* :meth:`set_duc_min`: Lower limit of the in-phase and quadrature
inputs to the DUC.
* :meth:`set_out_max`: Upper limit of the DAC output.
* :meth:`set_out_min`: Lower limit of the DAC output.
"""
self.set_duc_max_mu(int32(round(limit*self._duc_scale)))
@kernel
def set_duc_min(self, limit: TFloat):
""".. seealso:: :meth:`set_duc_max`"""
self.set_duc_min_mu(int32(round(limit*self._duc_scale)))
@kernel
def set_out_max(self, limit: TFloat):
""".. seealso:: :meth:`set_duc_max`"""
self.set_out_max_mu(int32(round(limit*self._out_scale)))
@kernel
def set_out_min(self, limit: TFloat):
""".. seealso:: :meth:`set_duc_max`"""
self.set_out_min_mu(int32(round(limit*self._out_scale)))
class SAWG:
"""Smart arbitrary waveform generator channel.
The channel is parametrized as: ::
oscillators = exp(2j*pi*(frequency0*t + phase0))*(
amplitude1*exp(2j*pi*(frequency1*t + phase1)) +
amplitude2*exp(2j*pi*(frequency2*t + phase2)))
output = (offset +
i_enable*Re(oscillators) +
q_enable*Im(buddy_oscillators))
This parametrization can be viewed as two complex (quadrature) oscillators
(``frequency1``/``phase1`` and ``frequency2``/``phase2``) that are
executing and sampling at the coarse RTIO frequency. They can represent
frequencies within the first Nyquist zone from ``-f_rtio_coarse/2`` to
``f_rtio_coarse/2``.
.. note:: The coarse RTIO frequency ``f_rtio_coarse`` is the inverse of
``ref_period*multiplier``. Both are arguments of the ``Core`` device,
specified in the device database ``device_db.py``.
The sum of their outputs is then interpolated by a factor of
:attr:`parallelism` (2, 4, 8 depending on the bitstream) using a
finite-impulse-response (FIR) anti-aliasing filter (more accurately
a half-band filter).
The filter is followed by a configurable saturating limiter.
After the limiter, the data is shifted in frequency using a complex
digital up-converter (DUC, ``frequency0``/``phase0``) running at
:attr:`parallelism` times the coarse RTIO frequency. The first Nyquist
zone of the DUC extends from ``-f_rtio_coarse*parallelism/2`` to
``f_rtio_coarse*parallelism/2``. Other Nyquist zones are usable depending
on the interpolation/modulation options configured in the DAC.
The real/in-phase data after digital up-conversion can be offset using
another spline interpolator ``offset``.
The ``i_enable``/``q_enable`` switches enable emission of quadrature
signals for later analog quadrature mixing distinguishing upper and lower
sidebands and thus doubling the bandwidth. They can also be used to emit
four-tone signals.
.. note:: Quadrature data from the buddy channel is currently
ignored in the SAWG gateware and not added to the DAC output.
This is equivalent to the ``q_enable`` switch always being ``0``.
The configuration channel and the nine
:class:`artiq.coredevice.spline.Spline` interpolators are accessible as
attributes:
* :attr:`config`: :class:`Config`
* :attr:`offset`, :attr:`amplitude1`, :attr:`amplitude2`: in units
of full scale
* :attr:`phase0`, :attr:`phase1`, :attr:`phase2`: in units of turns
* :attr:`frequency0`, :attr:`frequency1`, :attr:`frequency2`: in units
of Hz
.. note:: The latencies (pipeline depths) of the nine data channels (i.e.
all except :attr:`config`) are matched. Equivalent channels (e.g.
:attr:`phase1` and :attr:`phase2`) are exactly matched. Channels of
different type or functionality (e.g. :attr:`offset` vs
:attr:`amplitude1`, DDS vs DUC, :attr:`phase0` vs :attr:`phase1`) are
only matched to within one coarse RTIO cycle.
:param channel_base: RTIO channel number of the first channel (amplitude).
The configuration channel and frequency/phase/amplitude channels are
then assumed to be successive channels.
:param parallelism: Number of output samples per coarse RTIO clock cycle.
:param core_device: Name of the core device that this SAWG is on.
"""
kernel_invariants = {"channel_base", "core", "parallelism",
"amplitude1", "frequency1", "phase1",
"amplitude2", "frequency2", "phase2",
"frequency0", "phase0", "offset"}
def __init__(self, dmgr, channel_base, parallelism, core_device="core"):
self.core = dmgr.get(core_device)
self.channel_base = channel_base
self.parallelism = parallelism
width = 16
time_width = 16
cordic_gain = 1.646760258057163 # Cordic(width=16, guard=None).gain
head_room = 1.001
self.config = Config(channel_base, self.core, cordic_gain)
self.offset = Spline(width, time_width, channel_base + 1,
self.core, 2.*head_room)
self.amplitude1 = Spline(width, time_width, channel_base + 2,
self.core, 2*head_room*cordic_gain**2)
self.frequency1 = Spline(3*width, time_width, channel_base + 3,
self.core, 1/self.core.coarse_ref_period)
self.phase1 = Spline(width, time_width, channel_base + 4,
self.core, 1.)
self.amplitude2 = Spline(width, time_width, channel_base + 5,
self.core, 2*head_room*cordic_gain**2)
self.frequency2 = Spline(3*width, time_width, channel_base + 6,
self.core, 1/self.core.coarse_ref_period)
self.phase2 = Spline(width, time_width, channel_base + 7,
self.core, 1.)
self.frequency0 = Spline(2*width, time_width, channel_base + 8,
self.core,
parallelism/self.core.coarse_ref_period)
self.phase0 = Spline(width, time_width, channel_base + 9,
self.core, 1.)
@kernel
def reset(self):
"""Re-establish initial conditions.
This clears all spline interpolators, accumulators and configuration
settings.
This method advances the timeline by the time required to perform all
7 writes to the configuration channel, plus 9 coarse RTIO cycles.
"""
self.config.set_div(0, 0)
self.config.set_clr(1, 1, 1)
self.config.set_iq_en(1, 0)
self.config.set_duc_min(-1.)
self.config.set_duc_max(1.)
self.config.set_out_min(-1.)
self.config.set_out_max(1.)
self.frequency0.set_mu(0)
coarse_cycle = int64(self.core.ref_multiplier)
delay_mu(coarse_cycle)
self.frequency1.set_mu(0)
delay_mu(coarse_cycle)
self.frequency2.set_mu(0)
delay_mu(coarse_cycle)
self.phase0.set_mu(0)
delay_mu(coarse_cycle)
self.phase1.set_mu(0)
delay_mu(coarse_cycle)
self.phase2.set_mu(0)
delay_mu(coarse_cycle)
self.amplitude1.set_mu(0)
delay_mu(coarse_cycle)
self.amplitude2.set_mu(0)
delay_mu(coarse_cycle)
self.offset.set_mu(0)
delay_mu(coarse_cycle)

View File

@ -0,0 +1,54 @@
from artiq.language.core import kernel, delay
from artiq.language.units import us
class ShiftReg:
"""Driver for shift registers/latch combos connected to TTLs"""
kernel_invariants = {"dt", "n"}
def __init__(self, dmgr, clk, ser, latch, n=32, dt=10*us, ser_in=None):
self.core = dmgr.get("core")
self.clk = dmgr.get(clk)
self.ser = dmgr.get(ser)
self.latch = dmgr.get(latch)
self.n = n
self.dt = dt
if ser_in is not None:
self.ser_in = dmgr.get(ser_in)
@kernel
def set(self, data):
"""Sets the values of the latch outputs. This does not
advance the timeline and the waveform is generated before
`now`."""
delay(-2*(self.n + 1)*self.dt)
for i in range(self.n):
if (data >> (self.n-i-1)) & 1 == 0:
self.ser.off()
else:
self.ser.on()
self.clk.off()
delay(self.dt)
self.clk.on()
delay(self.dt)
self.clk.off()
self.latch.on()
delay(self.dt)
self.latch.off()
delay(self.dt)
@kernel
def get(self):
delay(-2*(self.n + 1)*self.dt)
data = 0
for i in range(self.n):
data <<= 1
self.ser_in.sample_input()
if self.ser_in.sample_get():
data |= 1
delay(self.dt)
self.clk.on()
delay(self.dt)
self.clk.off()
delay(self.dt)
return data

View File

@ -1,623 +0,0 @@
from artiq.language.core import *
from artiq.language.types import *
from artiq.coredevice.rtio import rtio_output, rtio_input_data
from artiq.coredevice import spi2 as spi
from artiq.language.units import us
@portable
def shuttler_volt_to_mu(volt):
"""Return the equivalent DAC code. Valid input range is from -10 to
10 - LSB.
"""
return round((1 << 16) * (volt / 20.0)) & 0xffff
class Config:
"""Shuttler configuration registers interface.
The configuration registers control waveform phase auto-clear, and pre-DAC
gain & offset values for calibration with ADC on the Shuttler AFE card.
To find the calibrated DAC code, the Shuttler Core first multiplies the
output data with pre-DAC gain, then adds the offset.
.. note::
The DAC code is capped at 0x7fff and 0x8000.
:param channel: RTIO channel number of this interface.
:param core_device: Core device name.
"""
kernel_invariants = {
"core", "channel", "target_base", "target_read",
"target_gain", "target_offset", "target_clr"
}
def __init__(self, dmgr, channel, core_device="core"):
self.core = dmgr.get(core_device)
self.channel = channel
self.target_base = channel << 8
self.target_read = 1 << 6
self.target_gain = 0 * (1 << 4)
self.target_offset = 1 * (1 << 4)
self.target_clr = 1 * (1 << 5)
@kernel
def set_clr(self, clr):
"""Set/Unset waveform phase clear bits.
Each bit corresponds to a Shuttler waveform generator core. Setting a
clear bit forces the Shuttler Core to clear the phase accumulator on
waveform trigger (See :class:`Trigger` for the trigger method).
Otherwise, the phase accumulator increments from its original value.
:param clr: Waveform phase clear bits. The MSB corresponds to Channel
15, LSB corresponds to Channel 0.
"""
rtio_output(self.target_base | self.target_clr, clr)
@kernel
def set_gain(self, channel, gain):
"""Set the 16-bits pre-DAC gain register of a Shuttler Core channel.
The `gain` parameter represents the decimal portion of the gain
factor. The MSB represents 0.5 and the sign bit. Hence, the valid
total gain value (1 +/- 0.gain) ranges from 0.5 to 1.5 - LSB.
:param channel: Shuttler Core channel to be configured.
:param gain: Shuttler Core channel gain.
"""
rtio_output(self.target_base | self.target_gain | channel, gain)
@kernel
def get_gain(self, channel):
"""Return the pre-DAC gain value of a Shuttler Core channel.
:param channel: The Shuttler Core channel.
:return: Pre-DAC gain value. See :meth:`set_gain`.
"""
rtio_output(self.target_base | self.target_gain |
self.target_read | channel, 0)
return rtio_input_data(self.channel)
@kernel
def set_offset(self, channel, offset):
"""Set the 16-bits pre-DAC offset register of a Shuttler Core channel.
.. seealso::
:meth:`shuttler_volt_to_mu`
:param channel: Shuttler Core channel to be configured.
:param offset: Shuttler Core channel offset.
"""
rtio_output(self.target_base | self.target_offset | channel, offset)
@kernel
def get_offset(self, channel):
"""Return the pre-DAC offset value of a Shuttler Core channel.
:param channel: The Shuttler Core channel.
:return: Pre-DAC offset value. See :meth:`set_offset`.
"""
rtio_output(self.target_base | self.target_offset |
self.target_read | channel, 0)
return rtio_input_data(self.channel)
class DCBias:
"""Shuttler Core cubic DC-bias spline.
A Shuttler channel can generate a waveform `w(t)` that is the sum of a
cubic spline `a(t)` and a sinusoid modulated in amplitude by a cubic
spline `b(t)` and in phase/frequency by a quadratic spline `c(t)`, where
.. math::
w(t) = a(t) + b(t) * cos(c(t))
And `t` corresponds to time in seconds.
This class controls the cubic spline `a(t)`, in which
.. math::
a(t) = p_0 + p_1t + \\frac{p_2t^2}{2} + \\frac{p_3t^3}{6}
And `a(t)` is in Volt.
:param channel: RTIO channel number of this DC-bias spline interface.
:param core_device: Core device name.
"""
kernel_invariants = {"core", "channel", "target_o"}
def __init__(self, dmgr, channel, core_device="core"):
self.core = dmgr.get(core_device)
self.channel = channel
self.target_o = channel << 8
@kernel
def set_waveform(self, a0: TInt32, a1: TInt32, a2: TInt64, a3: TInt64):
"""Set the DC-bias spline waveform.
Given `a(t)` as defined in :class:`DCBias`, the coefficients should be
configured by the following formulae.
.. math::
T &= 8*10^{-9}
a_0 &= p_0
a_1 &= p_1T + \\frac{p_2T^2}{2} + \\frac{p_3T^3}{6}
a_2 &= p_2T^2 + p_3T^3
a_3 &= p_3T^3
:math:`a_0`, :math:`a_1`, :math:`a_2` and :math:`a_3` are 16, 32, 48
and 48 bits in width respectively. See :meth:`shuttler_volt_to_mu` for
machine unit conversion.
Note: The waveform is not updated to the Shuttler Core until
triggered. See :class:`Trigger` for the update triggering mechanism.
:param a0: The :math:`a_0` coefficient in machine unit.
:param a1: The :math:`a_1` coefficient in machine unit.
:param a2: The :math:`a_2` coefficient in machine unit.
:param a3: The :math:`a_3` coefficient in machine unit.
"""
coef_words = [
a0,
a1,
a1 >> 16,
a2 & 0xFFFF,
(a2 >> 16) & 0xFFFF,
(a2 >> 32) & 0xFFFF,
a3 & 0xFFFF,
(a3 >> 16) & 0xFFFF,
(a3 >> 32) & 0xFFFF,
]
for i in range(len(coef_words)):
rtio_output(self.target_o | i, coef_words[i])
delay_mu(int64(self.core.ref_multiplier))
class DDS:
"""Shuttler Core DDS spline.
A Shuttler channel can generate a waveform `w(t)` that is the sum of a
cubic spline `a(t)` and a sinusoid modulated in amplitude by a cubic
spline `b(t)` and in phase/frequency by a quadratic spline `c(t)`, where
.. math::
w(t) = a(t) + b(t) * cos(c(t))
And `t` corresponds to time in seconds.
This class controls the cubic spline `b(t)` and quadratic spline `c(t)`,
in which
.. math::
b(t) &= g * (q_0 + q_1t + \\frac{q_2t^2}{2} + \\frac{q_3t^3}{6})
c(t) &= r_0 + r_1t + \\frac{r_2t^2}{2}
And `b(t)` is in Volt, `c(t)` is in number of turns. Note that `b(t)`
contributes to a constant gain of :math:`g=1.64676`.
:param channel: RTIO channel number of this DC-bias spline interface.
:param core_device: Core device name.
"""
kernel_invariants = {"core", "channel", "target_o"}
def __init__(self, dmgr, channel, core_device="core"):
self.core = dmgr.get(core_device)
self.channel = channel
self.target_o = channel << 8
@kernel
def set_waveform(self, b0: TInt32, b1: TInt32, b2: TInt64, b3: TInt64,
c0: TInt32, c1: TInt32, c2: TInt32):
"""Set the DDS spline waveform.
Given `b(t)` and `c(t)` as defined in :class:`DDS`, the coefficients
should be configured by the following formulae.
.. math::
T &= 8*10^{-9}
b_0 &= q_0
b_1 &= q_1T + \\frac{q_2T^2}{2} + \\frac{q_3T^3}{6}
b_2 &= q_2T^2 + q_3T^3
b_3 &= q_3T^3
c_0 &= r_0
c_1 &= r_1T + \\frac{r_2T^2}{2}
c_2 &= r_2T^2
:math:`b_0`, :math:`b_1`, :math:`b_2` and :math:`b_3` are 16, 32, 48
and 48 bits in width respectively. See :meth:`shuttler_volt_to_mu` for
machine unit conversion. :math:`c_0`, :math:`c_1` and :math:`c_2` are
16, 32 and 32 bits in width respectively.
Note: The waveform is not updated to the Shuttler Core until
triggered. See :class:`Trigger` for the update triggering mechanism.
:param b0: The :math:`b_0` coefficient in machine unit.
:param b1: The :math:`b_1` coefficient in machine unit.
:param b2: The :math:`b_2` coefficient in machine unit.
:param b3: The :math:`b_3` coefficient in machine unit.
:param c0: The :math:`c_0` coefficient in machine unit.
:param c1: The :math:`c_1` coefficient in machine unit.
:param c2: The :math:`c_2` coefficient in machine unit.
"""
coef_words = [
b0,
b1,
b1 >> 16,
b2 & 0xFFFF,
(b2 >> 16) & 0xFFFF,
(b2 >> 32) & 0xFFFF,
b3 & 0xFFFF,
(b3 >> 16) & 0xFFFF,
(b3 >> 32) & 0xFFFF,
c0,
c1,
c1 >> 16,
c2,
c2 >> 16,
]
for i in range(len(coef_words)):
rtio_output(self.target_o | i, coef_words[i])
delay_mu(int64(self.core.ref_multiplier))
class Trigger:
"""Shuttler Core spline coefficients update trigger.
:param channel: RTIO channel number of the trigger interface.
:param core_device: Core device name.
"""
kernel_invariants = {"core", "channel", "target_o"}
def __init__(self, dmgr, channel, core_device="core"):
self.core = dmgr.get(core_device)
self.channel = channel
self.target_o = channel << 8
@kernel
def trigger(self, trig_out):
"""Triggers coefficient update of (a) Shuttler Core channel(s).
Each bit corresponds to a Shuttler waveform generator core. Setting
`trig_out` bits commits the pending coefficient update (from
`set_waveform` in :class:`DCBias` and :class:`DDS`) to the Shuttler Core
synchronously.
:param trig_out: Coefficient update trigger bits. The MSB corresponds
to Channel 15, LSB corresponds to Channel 0.
"""
rtio_output(self.target_o, trig_out)
RELAY_SPI_CONFIG = (0*spi.SPI_OFFLINE | 1*spi.SPI_END |
0*spi.SPI_INPUT | 0*spi.SPI_CS_POLARITY |
0*spi.SPI_CLK_POLARITY | 0*spi.SPI_CLK_PHASE |
0*spi.SPI_LSB_FIRST | 0*spi.SPI_HALF_DUPLEX)
ADC_SPI_CONFIG = (0*spi.SPI_OFFLINE | 0*spi.SPI_END |
0*spi.SPI_INPUT | 0*spi.SPI_CS_POLARITY |
1*spi.SPI_CLK_POLARITY | 1*spi.SPI_CLK_PHASE |
0*spi.SPI_LSB_FIRST | 0*spi.SPI_HALF_DUPLEX)
# SPI clock write and read dividers
# CS should assert at least 9.5 ns after clk pulse
SPIT_RELAY_WR = 4
# 25 ns high/low pulse hold (limiting for write)
SPIT_ADC_WR = 4
SPIT_ADC_RD = 16
# SPI CS line
CS_RELAY = 1 << 0
CS_LED = 1 << 1
CS_ADC = 1 << 0
# Referenced AD4115 registers
_AD4115_REG_STATUS = 0x00
_AD4115_REG_ADCMODE = 0x01
_AD4115_REG_DATA = 0x04
_AD4115_REG_ID = 0x07
_AD4115_REG_CH0 = 0x10
_AD4115_REG_SETUPCON0 = 0x20
class Relay:
"""Shuttler AFE relay switches.
It controls the AFE relay switches and the LEDs. Switch on the relay to
enable AFE output; And off to disable the output. The LEDs indicates the
relay status.
.. note::
The relay does not disable ADC measurements. Voltage of any channels
can still be read by the ADC even after switching off the relays.
:param spi_device: SPI bus device name.
:param core_device: Core device name.
"""
kernel_invariant = {"core", "bus"}
def __init__(self, dmgr, spi_device, core_device="core"):
self.core = dmgr.get(core_device)
self.bus = dmgr.get(spi_device)
@kernel
def init(self):
"""Initialize SPI device.
Configures the SPI bus to 16-bits, write-only, simultaneous relay
switches and LED control.
"""
self.bus.set_config_mu(
RELAY_SPI_CONFIG, 16, SPIT_RELAY_WR, CS_RELAY | CS_LED)
@kernel
def enable(self, en: TInt32):
"""Enable/Disable relay switches of corresponding channels.
Each bit corresponds to the relay switch of a channel. Asserting a bit
turns on the corresponding relay switch; Deasserting the same bit
turns off the switch instead.
:param en: Switch enable bits. The MSB corresponds to Channel 15, LSB
corresponds to Channel 0.
"""
self.bus.write(en << 16)
class ADC:
"""Shuttler AFE ADC (AD4115) driver.
:param spi_device: SPI bus device name.
:param core_device: Core device name.
"""
kernel_invariant = {"core", "bus"}
def __init__(self, dmgr, spi_device, core_device="core"):
self.core = dmgr.get(core_device)
self.bus = dmgr.get(spi_device)
@kernel
def read_id(self) -> TInt32:
"""Read the product ID of the ADC.
The expected return value is 0x38DX, the 4 LSbs are don't cares.
:return: The read-back product ID.
"""
return self.read16(_AD4115_REG_ID)
@kernel
def reset(self):
"""AD4115 reset procedure.
This performs a write operation of 96 serial clock cycles with DIN
held at high. It resets the entire device, including the register
contents.
.. note::
The datasheet only requires 64 cycles, but reasserting `CS_n` right
after the transfer appears to interrupt the start-up sequence.
"""
self.bus.set_config_mu(ADC_SPI_CONFIG, 32, SPIT_ADC_WR, CS_ADC)
self.bus.write(0xffffffff)
self.bus.write(0xffffffff)
self.bus.set_config_mu(
ADC_SPI_CONFIG | spi.SPI_END, 32, SPIT_ADC_WR, CS_ADC)
self.bus.write(0xffffffff)
@kernel
def read8(self, addr: TInt32) -> TInt32:
"""Read from 8 bit register.
:param addr: Register address.
:return: Read-back register content.
"""
self.bus.set_config_mu(
ADC_SPI_CONFIG | spi.SPI_END | spi.SPI_INPUT,
16, SPIT_ADC_RD, CS_ADC)
self.bus.write((addr | 0x40) << 24)
return self.bus.read() & 0xff
@kernel
def read16(self, addr: TInt32) -> TInt32:
"""Read from 16 bit register.
:param addr: Register address.
:return: Read-back register content.
"""
self.bus.set_config_mu(
ADC_SPI_CONFIG | spi.SPI_END | spi.SPI_INPUT,
24, SPIT_ADC_RD, CS_ADC)
self.bus.write((addr | 0x40) << 24)
return self.bus.read() & 0xffff
@kernel
def read24(self, addr: TInt32) -> TInt32:
"""Read from 24 bit register.
:param addr: Register address.
:return: Read-back register content.
"""
self.bus.set_config_mu(
ADC_SPI_CONFIG | spi.SPI_END | spi.SPI_INPUT,
32, SPIT_ADC_RD, CS_ADC)
self.bus.write((addr | 0x40) << 24)
return self.bus.read() & 0xffffff
@kernel
def write8(self, addr: TInt32, data: TInt32):
"""Write to 8 bit register.
:param addr: Register address.
:param data: Data to be written.
"""
self.bus.set_config_mu(
ADC_SPI_CONFIG | spi.SPI_END, 16, SPIT_ADC_WR, CS_ADC)
self.bus.write(addr << 24 | (data & 0xff) << 16)
@kernel
def write16(self, addr: TInt32, data: TInt32):
"""Write to 16 bit register.
:param addr: Register address.
:param data: Data to be written.
"""
self.bus.set_config_mu(
ADC_SPI_CONFIG | spi.SPI_END, 24, SPIT_ADC_WR, CS_ADC)
self.bus.write(addr << 24 | (data & 0xffff) << 8)
@kernel
def write24(self, addr: TInt32, data: TInt32):
"""Write to 24 bit register.
:param addr: Register address.
:param data: Data to be written.
"""
self.bus.set_config_mu(
ADC_SPI_CONFIG | spi.SPI_END, 32, SPIT_ADC_WR, CS_ADC)
self.bus.write(addr << 24 | (data & 0xffffff))
@kernel
def read_ch(self, channel: TInt32) -> TFloat:
"""Sample a Shuttler channel on the AFE.
It performs a single conversion using profile 0 and setup 0, on the
selected channel. The sample is then recovered and converted to volt.
:param channel: Shuttler channel to be sampled.
:return: Voltage sample in volt.
"""
# Always configure Profile 0 for single conversion
self.write16(_AD4115_REG_CH0, 0x8000 | ((channel * 2 + 1) << 4))
self.write16(_AD4115_REG_SETUPCON0, 0x1300)
self.single_conversion()
delay(100*us)
adc_code = self.read24(_AD4115_REG_DATA)
return ((adc_code / (1 << 23)) - 1) * 2.5 / 0.1
@kernel
def single_conversion(self):
"""Place the ADC in single conversion mode.
The ADC returns to standby mode after the conversion is complete.
"""
self.write16(_AD4115_REG_ADCMODE, 0x8010)
@kernel
def standby(self):
"""Place the ADC in standby mode and disables power down the clock.
The ADC can be returned to single conversion mode by calling
:meth:`single_conversion`.
"""
# Selecting internal XO (0b00) also disables clock during standby
self.write16(_AD4115_REG_ADCMODE, 0x8020)
@kernel
def power_down(self):
"""Place the ADC in power-down mode.
The ADC must be reset before returning to other modes.
.. note::
The AD4115 datasheet suggests placing the ADC in standby mode
before power-down. This is to prevent accidental entry into the
power-down mode.
.. seealso::
:meth:`standby`
:meth:`power_up`
"""
self.write16(_AD4115_REG_ADCMODE, 0x8030)
@kernel
def power_up(self):
"""Exit the ADC power-down mode.
The ADC should be in power-down mode before calling this method.
.. seealso::
:meth:`power_down`
"""
self.reset()
# Although the datasheet claims 500 us reset wait time, only waiting
# for ~500 us can result in DOUT pin stuck in high
delay(2500*us)
@kernel
def calibrate(self, volts, trigger, config, samples=[-5.0, 0.0, 5.0]):
"""Calibrate the Shuttler waveform generator using the ADC on the AFE.
It finds the average slope rate and average offset by samples, and
compensate by writing the pre-DAC gain and offset registers in the
configuration registers.
.. note::
If the pre-calibration slope rate < 1, the calibration procedure
will introduce a pre-DAC gain compensation. However, this may
saturate the pre-DAC voltage code. (See :class:`Config` notes).
Shuttler cannot cover the entire +/- 10 V range in this case.
.. seealso::
:meth:`Config.set_gain`
:meth:`Config.set_offset`
:param volts: A list of all 16 cubic DC-bias spline.
(See :class:`DCBias`)
:param trigger: The Shuttler spline coefficient update trigger.
:param config: The Shuttler Core configuration registers.
:param samples: A list of sample voltages for calibration. There must
be at least 2 samples to perform slope rate calculation.
"""
assert len(volts) == 16
assert len(samples) > 1
measurements = [0.0] * len(samples)
for ch in range(16):
# Find the average slope rate and offset
for i in range(len(samples)):
self.core.break_realtime()
volts[ch].set_waveform(
shuttler_volt_to_mu(samples[i]), 0, 0, 0)
trigger.trigger(1 << ch)
measurements[i] = self.read_ch(ch)
# Find the average output slope
slope_sum = 0.0
for i in range(len(samples) - 1):
slope_sum += (measurements[i+1] - measurements[i])/(samples[i+1] - samples[i])
slope_avg = slope_sum / (len(samples) - 1)
gain_code = int32(1 / slope_avg * (2 ** 16)) & 0xffff
# Scale the measurements by 1/slope, find average offset
offset_sum = 0.0
for i in range(len(samples)):
offset_sum += (measurements[i] / slope_avg) - samples[i]
offset_avg = offset_sum / len(samples)
offset_code = shuttler_volt_to_mu(-offset_avg)
self.core.break_realtime()
config.set_gain(ch, gain_code)
delay_mu(int64(self.core.ref_multiplier))
config.set_offset(ch, offset_code)

View File

@ -72,10 +72,6 @@ class SPIMaster:
self.channel = channel
self.update_xfer_duration_mu(div, length)
@staticmethod
def get_rtio_channels(channel, **kwargs):
return [(channel, None)]
@portable
def frequency_to_div(self, f):
"""Convert a SPI clock frequency to the closest SPI clock divider."""
@ -277,8 +273,9 @@ class NRTSPIMaster:
def set_config_mu(self, flags=0, length=8, div=6, cs=1):
"""Set the ``config`` register.
In many cases, the SPI configuration is already set by the firmware
and you do not need to call this method.
Note that the non-realtime SPI cores are usually clocked by the system
clock and not the RTIO clock. In many cases, the SPI configuration is
already set by the firmware and you do not need to call this method.
"""
spi_set_config(self.busno, flags, length, div, cs)

228
artiq/coredevice/spline.py Normal file
View File

@ -0,0 +1,228 @@
from numpy import int32, int64
from artiq.language.core import kernel, portable, delay
from artiq.coredevice.rtio import rtio_output, rtio_output_wide
from artiq.language.types import TInt32, TInt64, TFloat
class Spline:
r"""Spline interpolating RTIO channel.
One knot of a polynomial basis spline (B-spline) :math:`u(t)`
is defined by the coefficients :math:`u_n` up to order :math:`n = k`.
If the coefficients are evaluated starting at time :math:`t_0`,
the output :math:`u(t)` for :math:`t > t_0, t_0` is:
.. math::
u(t) &= \sum_{n=0}^k \frac{u_n}{n!} (t - t_0)^n \\
&= u_0 + u_1 (t - t_0) + \frac{u_2}{2} (t - t_0)^2 + \dots
This class contains multiple methods to convert spline knot data from SI
to machine units and multiple methods that set the current spline
coefficient data. None of these advance the timeline. The :meth:`smooth`
method is the only method that advances the timeline.
:param width: Width in bits of the quantity that this spline controls
:param time_width: Width in bits of the time counter of this spline
:param channel: RTIO channel number
:param core_device: Core device that this spline is attached to
:param scale: Scale for conversion between machine units and physical
units; to be given as the "full scale physical value".
"""
kernel_invariants = {"channel", "core", "scale", "width",
"time_width", "time_scale"}
def __init__(self, width, time_width, channel, core_device, scale=1.):
self.core = core_device
self.channel = channel
self.width = width
self.scale = float((int64(1) << width) / scale)
self.time_width = time_width
self.time_scale = float((1 << time_width) *
core_device.coarse_ref_period)
@portable(flags={"fast-math"})
def to_mu(self, value: TFloat) -> TInt32:
"""Convert floating point ``value`` from physical units to 32 bit
integer machine units."""
return int32(round(value*self.scale))
@portable(flags={"fast-math"})
def from_mu(self, value: TInt32) -> TFloat:
"""Convert 32 bit integer ``value`` from machine units to floating
point physical units."""
return value/self.scale
@portable(flags={"fast-math"})
def to_mu64(self, value: TFloat) -> TInt64:
"""Convert floating point ``value`` from physical units to 64 bit
integer machine units."""
return int64(round(value*self.scale))
@kernel
def set_mu(self, value: TInt32):
"""Set spline value (machine units).
:param value: Spline value in integer machine units.
"""
rtio_output(self.channel << 8, value)
@kernel(flags={"fast-math"})
def set(self, value: TFloat):
"""Set spline value.
:param value: Spline value relative to full-scale.
"""
if self.width > 32:
l = [int32(0)] * 2
self.pack_coeff_mu([self.to_mu64(value)], l)
rtio_output_wide(self.channel << 8, l)
else:
rtio_output(self.channel << 8, self.to_mu(value))
@kernel
def set_coeff_mu(self, value): # TList(TInt32)
"""Set spline raw values.
:param value: Spline packed raw values.
"""
rtio_output_wide(self.channel << 8, value)
@portable(flags={"fast-math"})
def pack_coeff_mu(self, coeff, packed): # TList(TInt64), TList(TInt32)
"""Pack coefficients into RTIO data
:param coeff: TList(TInt64) list of machine units spline coefficients.
Lowest (zeroth) order first. The coefficient list is zero-extended
by the RTIO gateware.
:param packed: TList(TInt32) list for packed RTIO data. Must be
pre-allocated. Length in bits is
``n*width + (n - 1)*n//2*time_width``
"""
pos = 0
for i in range(len(coeff)):
wi = self.width + i*self.time_width
ci = coeff[i]
while wi != 0:
j = pos//32
used = pos - 32*j
avail = 32 - used
if avail > wi:
avail = wi
cij = int32(ci)
if avail != 32:
cij &= (1 << avail) - 1
packed[j] |= cij << used
ci >>= avail
wi -= avail
pos += avail
@portable(flags={"fast-math"})
def coeff_to_mu(self, coeff, coeff64): # TList(TFloat), TList(TInt64)
"""Convert a floating point list of coefficients into a 64 bit
integer (preallocated).
:param coeff: TList(TFloat) list of coefficients in physical units.
:param coeff64: TList(TInt64) preallocated list of coefficients in
machine units.
"""
for i in range(len(coeff)):
vi = coeff[i] * self.scale
for j in range(i):
vi *= self.time_scale
ci = int64(round(vi))
coeff64[i] = ci
# artiq.wavesynth.coefficients.discrete_compensate:
if i == 2:
coeff64[1] += ci >> self.time_width + 1
elif i == 3:
coeff64[2] += ci >> self.time_width
coeff64[1] += ci // 6 >> 2*self.time_width
def coeff_as_packed_mu(self, coeff64):
"""Pack 64 bit integer machine units coefficients into 32 bit integer
RTIO data list.
This is a host-only method that can be used to generate packed
spline coefficient data to be frozen into kernels at compile time.
"""
n = len(coeff64)
width = n*self.width + (n - 1)*n//2*self.time_width
packed = [int32(0)] * ((width + 31)//32)
self.pack_coeff_mu(coeff64, packed)
return packed
def coeff_as_packed(self, coeff):
"""Convert floating point spline coefficients into 32 bit integer
packed data.
This is a host-only method that can be used to generate packed
spline coefficient data to be frozen into kernels at compile time.
"""
coeff64 = [int64(0)] * len(coeff)
self.coeff_to_mu(coeff, coeff64)
return self.coeff_as_packed_mu(coeff64)
@kernel(flags={"fast-math"})
def set_coeff(self, coeff): # TList(TFloat)
"""Set spline coefficients.
Missing coefficients (high order) are zero-extended byt the RTIO
gateware.
If more coefficients are supplied than the gateware supports the extra
coefficients are ignored.
:param value: List of floating point spline coefficients,
lowest order (constant) coefficient first. Units are the
unit of this spline's value times increasing powers of 1/s.
"""
n = len(coeff)
coeff64 = [int64(0)] * n
self.coeff_to_mu(coeff, coeff64)
width = n*self.width + (n - 1)*n//2*self.time_width
packed = [int32(0)] * ((width + 31)//32)
self.pack_coeff_mu(coeff64, packed)
self.set_coeff_mu(packed)
@kernel(flags={"fast-math"})
def smooth(self, start: TFloat, stop: TFloat, duration: TFloat,
order: TInt32):
"""Initiate an interpolated value change.
For zeroth order (step) interpolation, the step is at
``start + duration/2``.
First order interpolation corresponds to a linear value ramp from
``start`` to ``stop`` over ``duration``.
The third order interpolation is constrained to have zero first
order derivative at both `start` and `stop`.
For first order and third order interpolation (linear and cubic)
the interpolator needs to be stopped explicitly at the stop time
(e.g. by setting spline coefficient data or starting a new
:meth:`smooth` interpolation).
This method advances the timeline by ``duration``.
:param start: Initial value of the change. In physical units.
:param stop: Final value of the change. In physical units.
:param duration: Duration of the interpolation. In physical units.
:param order: Order of the interpolation. Only 0, 1,
and 3 are valid: step, linear, cubic.
"""
if order == 0:
delay(duration/2.)
self.set_coeff([stop])
delay(duration/2.)
elif order == 1:
self.set_coeff([start, (stop - start)/duration])
delay(duration)
elif order == 3:
v2 = 6.*(stop - start)/(duration*duration)
self.set_coeff([start, 0., v2, -2.*v2/duration])
delay(duration)
else:
raise ValueError("Invalid interpolation order. "
"Supported orders are: 0, 1, 3.")

View File

@ -23,12 +23,12 @@ def y_mu_to_full_scale(y):
@portable
def adc_mu_to_volts(x, gain, corrected_fs=True):
def adc_mu_to_volts(x, gain):
"""Convert servo ADC data from machine units to Volt."""
val = (x >> 1) & 0xffff
mask = 1 << 15
val = -(val & mask) + (val & ~mask)
return sampler.adc_mu_to_volt(val, gain, corrected_fs)
return sampler.adc_mu_to_volt(val, gain)
class SUServo:
@ -62,15 +62,14 @@ class SUServo:
:param gains: Initial value for PGIA gains shift register
(default: 0x0000). Knowledge of this state is not transferred
between experiments.
:param sampler_hw_rev: Sampler's revision string
:param core_device: Core device name
"""
kernel_invariants = {"channel", "core", "pgia", "cplds", "ddses",
"ref_period_mu", "corrected_fs"}
"ref_period_mu"}
def __init__(self, dmgr, channel, pgia_device,
cpld_devices, dds_devices,
gains=0x0000, sampler_hw_rev="v2.2", core_device="core"):
gains=0x0000, core_device="core"):
self.core = dmgr.get(core_device)
self.pgia = dmgr.get(pgia_device)
@ -82,13 +81,8 @@ class SUServo:
self.gains = gains
self.ref_period_mu = self.core.seconds_to_mu(
self.core.coarse_ref_period)
self.corrected_fs = sampler.Sampler.use_corrected_fs(sampler_hw_rev)
assert self.ref_period_mu == self.core.ref_multiplier
@staticmethod
def get_rtio_channels(channel, **kwargs):
return [(channel, None)]
@kernel
def init(self):
"""Initialize the servo, Sampler and both Urukuls.
@ -240,7 +234,7 @@ class SUServo:
"""
val = self.get_adc_mu(channel)
gain = (self.gains >> (channel*2)) & 0b11
return adc_mu_to_volts(val, gain, self.corrected_fs)
return adc_mu_to_volts(val, gain)
class Channel:
@ -261,10 +255,6 @@ class Channel:
self.servo.channel)
self.dds = self.servo.ddses[self.servo_channel // 4]
@staticmethod
def get_rtio_channels(channel, **kwargs):
return [(channel, None)]
@kernel
def set(self, en_out, en_iir=0, profile=0):
"""Operate channel.

View File

@ -36,10 +36,6 @@ class TTLOut:
self.channel = channel
self.target_o = channel << 8
@staticmethod
def get_rtio_channels(channel, **kwargs):
return [(channel, None)]
@kernel
def output(self):
pass
@ -132,10 +128,6 @@ class TTLInOut:
self.target_sens = (channel << 8) + 2
self.target_sample = (channel << 8) + 3
@staticmethod
def get_rtio_channels(channel, **kwargs):
return [(channel, None)]
@kernel
def set_oe(self, oe):
rtio_output(self.target_oe, 1 if oe else 0)
@ -473,10 +465,6 @@ class TTLClockGen:
self.acc_width = numpy.int64(acc_width)
@staticmethod
def get_rtio_channels(channel, **kwargs):
return [(channel, None)]
@portable
def frequency_to_ftw(self, frequency):
"""Returns the frequency tuning word corresponding to the given

View File

@ -5,7 +5,7 @@ import numpy as np
from PyQt5 import QtCore, QtWidgets
from sipyco import pyon
from artiq.tools import scale_from_metadata, short_format, exc_to_warning
from artiq.tools import short_format, exc_to_warning
from artiq.gui.tools import LayoutWidget, QRecursiveFilterProxyModel
from artiq.gui.models import DictSyncTreeSepModel
from artiq.gui.scientific_spinbox import ScientificSpinBox
@ -14,18 +14,92 @@ from artiq.gui.scientific_spinbox import ScientificSpinBox
logger = logging.getLogger(__name__)
async def rename(key, new_key, value, metadata, persist, dataset_ctl):
if key != new_key:
async def rename(key, newkey, value, dataset_ctl):
if key != newkey:
await dataset_ctl.delete(key)
await dataset_ctl.set(new_key, value, metadata=metadata, persist=persist)
await dataset_ctl.set(newkey, value)
class CreateEditDialog(QtWidgets.QDialog):
def __init__(self, parent, dataset_ctl, key=None, value=None, metadata=None, persist=False):
class Editor(QtWidgets.QDialog):
def __init__(self, parent, dataset_ctl, key, value):
QtWidgets.QDialog.__init__(self, parent=parent)
self.dataset_ctl = dataset_ctl
self.key = key
self.initial_type = type(value)
self.setWindowTitle("Edit dataset")
grid = QtWidgets.QGridLayout()
self.setLayout(grid)
grid.addWidget(QtWidgets.QLabel("Name:"), 0, 0)
self.name_widget = QtWidgets.QLineEdit()
self.name_widget.setText(key)
grid.addWidget(self.name_widget, 0, 1)
grid.addWidget(QtWidgets.QLabel("Value:"), 1, 0)
grid.addWidget(self.get_edit_widget(value), 1, 1)
buttons = QtWidgets.QDialogButtonBox(
QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
grid.setRowStretch(2, 1)
grid.addWidget(buttons, 3, 0, 1, 2)
buttons.accepted.connect(self.accept)
buttons.rejected.connect(self.reject)
def accept(self):
newkey = self.name_widget.text()
value = self.initial_type(self.get_edit_widget_value())
asyncio.ensure_future(rename(self.key, newkey, value, self.dataset_ctl))
QtWidgets.QDialog.accept(self)
def get_edit_widget(self, initial_value):
raise NotImplementedError
def get_edit_widget_value(self):
raise NotImplementedError
class NumberEditor(Editor):
def get_edit_widget(self, initial_value):
self.edit_widget = ScientificSpinBox()
self.edit_widget.setDecimals(13)
self.edit_widget.setPrecision()
self.edit_widget.setRelativeStep()
self.edit_widget.setValue(float(initial_value))
return self.edit_widget
def get_edit_widget_value(self):
return self.edit_widget.value()
class BoolEditor(Editor):
def get_edit_widget(self, initial_value):
self.edit_widget = QtWidgets.QCheckBox()
self.edit_widget.setChecked(bool(initial_value))
return self.edit_widget
def get_edit_widget_value(self):
return self.edit_widget.isChecked()
class StringEditor(Editor):
def get_edit_widget(self, initial_value):
self.edit_widget = QtWidgets.QLineEdit()
self.edit_widget.setText(initial_value)
return self.edit_widget
def get_edit_widget_value(self):
return self.edit_widget.text()
class Creator(QtWidgets.QDialog):
def __init__(self, parent, dataset_ctl):
QtWidgets.QDialog.__init__(self, parent=parent)
self.dataset_ctl = dataset_ctl
self.setWindowTitle("Create dataset" if key is None else "Edit dataset")
self.setWindowTitle("Create dataset")
grid = QtWidgets.QGridLayout()
grid.setRowMinimumHeight(1, 40)
grid.setColumnMinimumWidth(2, 60)
@ -43,21 +117,9 @@ class CreateEditDialog(QtWidgets.QDialog):
grid.addWidget(self.data_type, 1, 2)
self.value_widget.textChanged.connect(self.dtype)
grid.addWidget(QtWidgets.QLabel("Unit:"), 2, 0)
self.unit_widget = QtWidgets.QLineEdit()
grid.addWidget(self.unit_widget, 2, 1)
grid.addWidget(QtWidgets.QLabel("Scale:"), 3, 0)
self.scale_widget = QtWidgets.QLineEdit()
grid.addWidget(self.scale_widget, 3, 1)
grid.addWidget(QtWidgets.QLabel("Precision:"), 4, 0)
self.precision_widget = QtWidgets.QLineEdit()
grid.addWidget(self.precision_widget, 4, 1)
grid.addWidget(QtWidgets.QLabel("Persist:"), 5, 0)
grid.addWidget(QtWidgets.QLabel("Persist:"), 2, 0)
self.box_widget = QtWidgets.QCheckBox()
grid.addWidget(self.box_widget, 5, 1)
grid.addWidget(self.box_widget, 2, 1)
self.ok = QtWidgets.QPushButton('&Ok')
self.ok.setEnabled(False)
@ -67,62 +129,23 @@ class CreateEditDialog(QtWidgets.QDialog):
self.ok, QtWidgets.QDialogButtonBox.AcceptRole)
self.buttons.addButton(
self.cancel, QtWidgets.QDialogButtonBox.RejectRole)
grid.setRowStretch(6, 1)
grid.addWidget(self.buttons, 7, 0, 1, 3, alignment=QtCore.Qt.AlignHCenter)
grid.setRowStretch(3, 1)
grid.addWidget(self.buttons, 4, 0, 1, 3)
self.buttons.accepted.connect(self.accept)
self.buttons.rejected.connect(self.reject)
self.key = key
self.name_widget.setText(key)
value_edit_string = self.value_to_edit_string(value)
if metadata is not None:
scale = scale_from_metadata(metadata)
t = value.dtype if value is np.ndarray else type(value)
if scale != 1 and np.issubdtype(t, np.number):
# degenerates to float type
value_edit_string = self.value_to_edit_string(
np.float64(value / scale))
self.unit_widget.setText(metadata.get('unit', ''))
self.scale_widget.setText(str(metadata.get('scale', '')))
self.precision_widget.setText(str(metadata.get('precision', '')))
self.value_widget.setText(value_edit_string)
self.box_widget.setChecked(persist)
def accept(self):
key = self.name_widget.text()
value = self.value_widget.text()
persist = self.box_widget.isChecked()
unit = self.unit_widget.text()
scale = self.scale_widget.text()
precision = self.precision_widget.text()
metadata = {}
if unit != "":
metadata['unit'] = unit
if scale != "":
metadata['scale'] = float(scale)
if precision != "":
metadata['precision'] = int(precision)
scale = scale_from_metadata(metadata)
value = self.parse_edit_string(value)
t = value.dtype if value is np.ndarray else type(value)
if scale != 1 and np.issubdtype(t, np.number):
# degenerates to float type
value = np.float64(value * scale)
if self.key and self.key != key:
asyncio.ensure_future(exc_to_warning(rename(self.key, key, value, metadata, persist, self.dataset_ctl)))
else:
asyncio.ensure_future(exc_to_warning(self.dataset_ctl.set(key, value, metadata=metadata, persist=persist)))
self.key = key
asyncio.ensure_future(exc_to_warning(self.dataset_ctl.set(
key, pyon.decode(value), persist)))
QtWidgets.QDialog.accept(self)
def dtype(self):
txt = self.value_widget.text()
try:
result = self.parse_edit_string(txt)
# ensure only pyon compatible types are permissable
pyon.encode(result)
result = pyon.decode(txt)
except:
pixmap = self.style().standardPixmap(
QtWidgets.QStyle.SP_MessageBoxWarning)
@ -132,35 +155,6 @@ class CreateEditDialog(QtWidgets.QDialog):
self.data_type.setText(type(result).__name__)
self.ok.setEnabled(True)
@staticmethod
def parse_edit_string(s):
if s == "":
raise TypeError
_eval_dict = {
"__builtins__": {},
"array": np.array,
"null": np.nan,
"inf": np.inf
}
for t_ in pyon._numpy_scalar:
_eval_dict[t_] = eval("np.{}".format(t_), {"np": np})
return eval(s, _eval_dict, {})
@staticmethod
def value_to_edit_string(v):
t = type(v)
r = ""
if isinstance(v, np.generic):
r += t.__name__
r += "("
r += repr(v)
r += ")"
elif v is None:
return r
else:
r += repr(v)
return r
class Model(DictSyncTreeSepModel):
def __init__(self, init):
@ -172,13 +166,13 @@ class Model(DictSyncTreeSepModel):
if column == 1:
return "Y" if v[0] else "N"
elif column == 2:
return short_format(v[1], v[2])
return short_format(v[1])
else:
raise ValueError
class DatasetsDock(QtWidgets.QDockWidget):
def __init__(self, dataset_sub, dataset_ctl):
def __init__(self, datasets_sub, dataset_ctl):
QtWidgets.QDockWidget.__init__(self, "Datasets")
self.setObjectName("Datasets")
self.setFeatures(QtWidgets.QDockWidget.DockWidgetMovable |
@ -218,7 +212,7 @@ class DatasetsDock(QtWidgets.QDockWidget):
self.table.addAction(delete_action)
self.table_model = Model(dict())
dataset_sub.add_setmodel_callback(self.set_model)
datasets_sub.add_setmodel_callback(self.set_model)
def _search_datasets(self):
if hasattr(self, "table_model_filter"):
@ -232,7 +226,7 @@ class DatasetsDock(QtWidgets.QDockWidget):
self.table.setModel(self.table_model_filter)
def create_clicked(self):
CreateEditDialog(self, self.dataset_ctl).open()
Creator(self, self.dataset_ctl).open()
def edit_clicked(self):
idx = self.table.selectedIndexes()
@ -240,8 +234,19 @@ class DatasetsDock(QtWidgets.QDockWidget):
idx = self.table_model_filter.mapToSource(idx[0])
key = self.table_model.index_to_key(idx)
if key is not None:
persist, value, metadata = self.table_model.backing_store[key]
CreateEditDialog(self, self.dataset_ctl, key, value, metadata, persist).open()
persist, value = self.table_model.backing_store[key]
t = type(value)
if np.issubdtype(t, np.number):
dialog_cls = NumberEditor
elif np.issubdtype(t, np.bool_):
dialog_cls = BoolEditor
elif np.issubdtype(t, np.unicode_):
dialog_cls = StringEditor
else:
logger.error("Cannot edit dataset %s: "
"type %s is not supported", key, t)
return
dialog_cls(self, self.dataset_ctl, key, value).open()
def delete_clicked(self):
idx = self.table.selectedIndexes()

View File

@ -11,9 +11,7 @@ from sipyco import pyon
from artiq.gui.entries import procdesc_to_entry, ScanEntry
from artiq.gui.fuzzy_select import FuzzySelectWidget
from artiq.gui.tools import (LayoutWidget, WheelFilter,
log_level_to_name, get_open_file_name)
from artiq.tools import parse_devarg_override, unparse_devarg_override
from artiq.gui.tools import LayoutWidget, log_level_to_name, get_open_file_name
logger = logging.getLogger(__name__)
@ -25,6 +23,15 @@ logger = logging.getLogger(__name__)
# 2. file:<class name>@<file name>
class _WheelFilter(QtCore.QObject):
def eventFilter(self, obj, event):
if (event.type() == QtCore.QEvent.Wheel and
event.modifiers() != QtCore.Qt.NoModifier):
event.ignore()
return True
return False
class _ArgumentEditor(QtWidgets.QTreeWidget):
def __init__(self, manager, dock, expurl):
self.manager = manager
@ -48,7 +55,7 @@ class _ArgumentEditor(QtWidgets.QTreeWidget):
self.setStyleSheet("QTreeWidget {background: " +
self.palette().midlight().color().name() + " ;}")
self.viewport().installEventFilter(WheelFilter(self.viewport(), True))
self.viewport().installEventFilter(_WheelFilter(self.viewport()))
self._groups = dict()
self._arg_to_widgets = dict()
@ -152,23 +159,6 @@ class _ArgumentEditor(QtWidgets.QTreeWidget):
self._groups[name] = group
return group
def update_argument(self, name, argument):
widgets = self._arg_to_widgets[name]
# Qt needs a setItemWidget() to handle layout correctly,
# simply replacing the entry inside the LayoutWidget
# results in a bug.
widgets["entry"].deleteLater()
widgets["entry"] = procdesc_to_entry(argument["desc"])(argument)
widgets["disable_other_scans"].setVisible(
isinstance(widgets["entry"], ScanEntry))
widgets["fix_layout"].deleteLater()
widgets["fix_layout"] = LayoutWidget()
widgets["fix_layout"].addWidget(widgets["entry"])
self.setItemWidget(widgets["widget_item"], 1, widgets["fix_layout"])
self.updateGeometries()
def _recompute_argument_clicked(self, name):
asyncio.ensure_future(self._recompute_argument(name))
@ -185,7 +175,22 @@ class _ArgumentEditor(QtWidgets.QTreeWidget):
state = procdesc_to_entry(procdesc).default_state(procdesc)
argument["desc"] = procdesc
argument["state"] = state
self.update_argument(name, argument)
# Qt needs a setItemWidget() to handle layout correctly,
# simply replacing the entry inside the LayoutWidget
# results in a bug.
widgets = self._arg_to_widgets[name]
widgets["entry"].deleteLater()
widgets["entry"] = procdesc_to_entry(procdesc)(argument)
widgets["disable_other_scans"].setVisible(
isinstance(widgets["entry"], ScanEntry))
widgets["fix_layout"].deleteLater()
widgets["fix_layout"] = LayoutWidget()
widgets["fix_layout"].addWidget(widgets["entry"])
self.setItemWidget(widgets["widget_item"], 1, widgets["fix_layout"])
self.updateGeometries()
def _disable_other_scans(self, current_name):
for name, widgets in self._arg_to_widgets.items():
@ -306,7 +311,7 @@ class _ExperimentDock(QtWidgets.QMdiSubWindow):
flush = self.flush
flush.setToolTip("Flush the pipeline (of current- and higher-priority "
"experiments) before starting the experiment")
self.layout.addWidget(flush, 2, 2)
self.layout.addWidget(flush, 2, 2, 1, 2)
flush.setChecked(scheduling["flush"])
@ -314,20 +319,6 @@ class _ExperimentDock(QtWidgets.QMdiSubWindow):
scheduling["flush"] = bool(checked)
flush.stateChanged.connect(update_flush)
devarg_override = QtWidgets.QComboBox()
devarg_override.setEditable(True)
devarg_override.lineEdit().setPlaceholderText("Override device arguments")
devarg_override.lineEdit().setClearButtonEnabled(True)
devarg_override.insertItem(0, "core:analyze_at_run_end=True")
self.layout.addWidget(devarg_override, 2, 3)
devarg_override.setCurrentText(options["devarg_override"])
def update_devarg_override(text):
options["devarg_override"] = text
devarg_override.editTextChanged.connect(update_devarg_override)
self.devarg_override = devarg_override
log_level = QtWidgets.QComboBox()
log_level.addItems(log_levels)
log_level.setCurrentIndex(1)
@ -348,7 +339,6 @@ class _ExperimentDock(QtWidgets.QMdiSubWindow):
if "repo_rev" in options:
repo_rev = QtWidgets.QLineEdit()
repo_rev.setPlaceholderText("current")
repo_rev.setClearButtonEnabled(True)
repo_rev_label = QtWidgets.QLabel("Revision:")
repo_rev_label.setToolTip("Experiment repository revision "
"(commit ID) to use")
@ -481,9 +471,6 @@ class _ExperimentDock(QtWidgets.QMdiSubWindow):
return
try:
if "devarg_override" in expid:
self.devarg_override.setCurrentText(
unparse_devarg_override(expid["devarg_override"]))
self.log_level.setCurrentIndex(log_levels.index(
log_level_to_name(expid["log_level"])))
if ("repo_rev" in expid and
@ -657,8 +644,7 @@ class ExperimentManager:
else:
# mutated by _ExperimentDock
options = {
"log_level": logging.WARNING,
"devarg_override": ""
"log_level": logging.WARNING
}
if expurl[:5] == "repo:":
options["repo_rev"] = None
@ -678,20 +664,6 @@ class ExperimentManager:
self.submission_arguments[expurl] = arguments
self.argument_ui_names[expurl] = ui_name
return arguments
def set_argument_value(self, expurl, name, value):
try:
argument = self.submission_arguments[expurl][name]
if argument["desc"]["ty"] == "Scannable":
ty = value["ty"]
argument["state"]["selected"] = ty
argument["state"][ty] = value
else:
argument["state"] = value
if expurl in self.open_experiments.keys():
self.open_experiments[expurl].argeditor.update_argument(name, argument)
except:
logger.warn("Failed to set value for argument \"{}\" in experiment: {}.".format(name, expurl), exc_info=1)
def get_submission_arguments(self, expurl):
if expurl in self.submission_arguments:
@ -753,14 +725,7 @@ class ExperimentManager:
entry_cls = procdesc_to_entry(argument["desc"])
argument_values[name] = entry_cls.state_to_value(argument["state"])
try:
devarg_override = parse_devarg_override(options["devarg_override"])
except:
logger.error("Failed to parse device argument overrides for %s", expurl)
return
expid = {
"devarg_override": devarg_override,
"log_level": options["log_level"],
"file": file,
"class_name": class_name,

View File

@ -7,11 +7,7 @@ device_db = {
"type": "local",
"module": "artiq.coredevice.core",
"class": "Core",
"arguments": {
"host": core_addr,
"ref_period": 1e-9,
"analyzer_proxy": "core_analyzer"
}
"arguments": {"host": core_addr, "ref_period": 1e-9}
},
"core_log": {
"type": "controller",
@ -26,13 +22,6 @@ device_db = {
"port": 1384,
"command": "aqctl_moninj_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr
},
"core_analyzer": {
"type": "controller",
"host": "::1",
"port_proxy": 1385,
"port": 1386,
"command": "aqctl_coreanalyzer_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr
},
"core_cache": {
"type": "local",
"module": "artiq.coredevice.cache",

View File

@ -5,11 +5,7 @@ device_db = {
"type": "local",
"module": "artiq.coredevice.core",
"class": "Core",
"arguments": {
"host": core_addr,
"ref_period": 1e-9,
"analyzer_proxy": "core_analyzer"
}
"arguments": {"host": core_addr, "ref_period": 1/(8*150e6)}
},
"core_log": {
"type": "controller",
@ -24,13 +20,6 @@ device_db = {
"port": 1384,
"command": "aqctl_moninj_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr
},
"core_analyzer": {
"type": "controller",
"host": "::1",
"port_proxy": 1385,
"port": 1386,
"command": "aqctl_coreanalyzer_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr
},
"core_cache": {
"type": "local",
"module": "artiq.coredevice.cache",

View File

@ -0,0 +1,184 @@
core_addr = "192.168.1.70"
device_db = {
"core": {
"type": "local",
"module": "artiq.coredevice.core",
"class": "Core",
"arguments": {"host": core_addr, "ref_period": 1/(8*150e6)}
},
"core_log": {
"type": "controller",
"host": "::1",
"port": 1068,
"command": "aqctl_corelog -p {port} --bind {bind} " + core_addr
},
"core_moninj": {
"type": "controller",
"host": "::1",
"port_proxy": 1383,
"port": 1384,
"command": "aqctl_moninj_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr
},
"core_cache": {
"type": "local",
"module": "artiq.coredevice.cache",
"class": "CoreCache"
},
"core_dma": {
"type": "local",
"module": "artiq.coredevice.dma",
"class": "CoreDMA"
},
}
device_db.update(
spi_urukul0={
"type": "local",
"module": "artiq.coredevice.spi2",
"class": "SPIMaster",
"arguments": {"channel": 0}
},
ttl_urukul0_io_update={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 1}
},
ttl_urukul0_sw0={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 2}
},
ttl_urukul0_sw1={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 3}
},
ttl_urukul0_sw2={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 4}
},
ttl_urukul0_sw3={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 5}
},
urukul0_cpld={
"type": "local",
"module": "artiq.coredevice.urukul",
"class": "CPLD",
"arguments": {
"spi_device": "spi_urukul0",
"io_update_device": "ttl_urukul0_io_update",
"refclk": 150e6,
"clk_sel": 2
}
}
)
for i in range(4):
device_db["urukul0_ch" + str(i)] = {
"type": "local",
"module": "artiq.coredevice.ad9910",
"class": "AD9910",
"arguments": {
"pll_n": 16, # 600MHz sample rate
"pll_vco": 2,
"chip_select": 4 + i,
"cpld_device": "urukul0_cpld",
"sw_device": "ttl_urukul0_sw" + str(i)
}
}
"""
artiq_route routing.bin init
artiq_route routing.bin set 0 0
artiq_route routing.bin set 1 1 0
artiq_route routing.bin set 2 1 1 0
artiq_route routing.bin set 3 2 0
artiq_route routing.bin set 4 2 1 0
artiq_coremgmt -D kasli config write -f routing_table routing.bin
"""
for sayma in range(2):
amc_base = 0x010000 + sayma*0x020000
rtm_base = 0x020000 + sayma*0x020000
for i in range(4):
device_db["led" + str(4*sayma+i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": amc_base + i}
}
for i in range(2):
device_db["ttl_mcx" + str(2*sayma+i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLInOut",
"arguments": {"channel": amc_base + 4 + i}
}
for i in range(8):
device_db["sawg" + str(8*sayma+i)] = {
"type": "local",
"module": "artiq.coredevice.sawg",
"class": "SAWG",
"arguments": {"channel_base": amc_base + 6 + i*10, "parallelism": 4}
}
for basemod in range(2):
for i in range(4):
device_db["sawg_sw" + str(8*sayma+4*basemod+i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": rtm_base + basemod*9 + i}
}
att_idx = 2*sayma + basemod
device_db["basemod_att_rst_n"+str(att_idx)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": rtm_base + basemod*9 + 4}
}
device_db["basemod_att_clk"+str(att_idx)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": rtm_base + basemod*9 + 5}
}
device_db["basemod_att_le"+str(att_idx)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": rtm_base + basemod*9 + 6}
}
device_db["basemod_att_mosi"+str(att_idx)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": rtm_base + basemod*9 + 7}
}
device_db["basemod_att_miso"+str(att_idx)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLInOut",
"arguments": {"channel": rtm_base + basemod*9 + 8}
}
device_db["basemod_att"+str(att_idx)] = {
"type": "local",
"module": "artiq.coredevice.basemod_att",
"class": "BaseModAtt",
"arguments": {
"rst_n": "basemod_att_rst_n"+str(att_idx),
"clk": "basemod_att_clk"+str(att_idx),
"le": "basemod_att_le"+str(att_idx),
"mosi": "basemod_att_mosi"+str(att_idx),
"miso": "basemod_att_miso"+str(att_idx),
}
}

View File

@ -0,0 +1,25 @@
from artiq.experiment import *
class BaseMod(EnvExperiment):
def build(self):
self.setattr_device("core")
self.basemods = [self.get_device("basemod_att0"), self.get_device("basemod_att1")]
self.rfsws = [self.get_device("sawg_sw"+str(i)) for i in range(8)]
@kernel
def run(self):
self.core.reset()
for basemod in self.basemods:
self.core.break_realtime()
delay(10*ms)
basemod.reset()
delay(10*ms)
basemod.set(0.0, 0.0, 0.0, 0.0)
delay(10*ms)
print(basemod.get_mu())
self.core.break_realtime()
for rfsw in self.rfsws:
rfsw.on()
delay(1*ms)

View File

@ -0,0 +1,37 @@
from artiq.experiment import *
class Sines2Sayma(EnvExperiment):
def build(self):
self.setattr_device("core")
self.sawgs = [self.get_device("sawg"+str(i)) for i in range(16)]
@kernel
def drtio_is_up(self):
for i in range(5):
if not self.core.get_rtio_destination_status(i):
return False
return True
@kernel
def run(self):
while True:
print("waiting for DRTIO ready...")
while not self.drtio_is_up():
pass
print("OK")
self.core.reset()
for sawg in self.sawgs:
delay(1*ms)
sawg.reset()
for sawg in self.sawgs:
delay(1*ms)
sawg.amplitude1.set(.4)
# Do not use a sub-multiple of oscilloscope sample rates.
sawg.frequency0.set(9*MHz)
while self.drtio_is_up():
pass

View File

@ -0,0 +1,89 @@
from artiq.experiment import *
class SinesUrukulSayma(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("urukul0_cpld")
# Urukul clock output syntonized to the RTIO clock.
# Can be used as HMC830 reference on Sayma RTM.
# When using this reference, Sayma must be recalibrated every time Urukul
# is rebooted, as Urukul is not synchronized to the Kasli.
self.urukul_hmc_ref = self.get_device("urukul0_ch3")
# Urukul measurement channels - compare with SAWG outputs.
# When testing sync, do not reboot Urukul, as it is not
# synchronized to the Kasli.
self.urukul_meas = [self.get_device("urukul0_ch" + str(i)) for i in range(3)]
# The same waveform is output on all first 4 SAWG channels (first DAC).
self.sawgs = [self.get_device("sawg"+str(i)) for i in range(4)]
self.basemod = self.get_device("basemod_att0")
self.rfsws = [self.get_device("sawg_sw"+str(i)) for i in range(4)]
# DRTIO destinations:
# 0: local
# 1: Sayma AMC
# 2: Sayma RTM
@kernel
def drtio_is_up(self):
for i in range(3):
if not self.core.get_rtio_destination_status(i):
return False
return True
@kernel
def run(self):
f = 9*MHz
dds_ftw = self.urukul_meas[0].frequency_to_ftw(f)
sawg_ftw = self.sawgs[0].frequency0.to_mu(f)
if dds_ftw != sawg_ftw:
print("DDS and SAWG FTWs do not match:", dds_ftw, sawg_ftw)
return
self.core.reset()
self.urukul0_cpld.init()
delay(1*ms)
self.urukul_hmc_ref.init()
self.urukul_hmc_ref.set_mu(0x40000000, asf=self.urukul_hmc_ref.amplitude_to_asf(0.6))
self.urukul_hmc_ref.set_att(6.)
self.urukul_hmc_ref.sw.on()
for urukul_ch in self.urukul_meas:
delay(1*ms)
urukul_ch.init()
urukul_ch.set_mu(dds_ftw, asf=urukul_ch.amplitude_to_asf(0.5))
urukul_ch.set_att(6.)
urukul_ch.sw.on()
while True:
print("waiting for DRTIO ready...")
while not self.drtio_is_up():
pass
print("OK")
self.core.reset()
delay(10*ms)
self.basemod.reset()
delay(10*ms)
self.basemod.set(3.0, 3.0, 3.0, 3.0)
delay(10*ms)
for rfsw in self.rfsws:
delay(1*ms)
rfsw.on()
for sawg in self.sawgs:
delay(1*ms)
sawg.reset()
for sawg in self.sawgs:
delay(1*ms)
sawg.amplitude1.set(.4)
sawg.frequency0.set_mu(sawg_ftw)
sawg.phase0.set_mu(sawg_ftw*now_mu() >> 17)
while self.drtio_is_up():
pass

View File

@ -1,18 +0,0 @@
{
"target": "kasli",
"variant": "shuttlerdemo",
"hw_rev": "v2.0",
"drtio_role": "master",
"peripherals": [
{
"type": "shuttler",
"ports": [0]
},
{
"type": "dio",
"ports": [1],
"bank_direction_low": "input",
"bank_direction_high": "output"
}
]
}

View File

@ -1,330 +0,0 @@
from artiq.experiment import *
from artiq.coredevice.shuttler import shuttler_volt_to_mu
DAC_Fs_MHZ = 125
CORDIC_GAIN = 1.64676
@portable
def shuttler_phase_offset(offset_degree):
return round(offset_degree / 360 * (2 ** 16))
@portable
def shuttler_freq_mu(freq_mhz):
return round(float(2) ** 32 / DAC_Fs_MHZ * freq_mhz)
@portable
def shuttler_chirp_rate_mu(freq_mhz_per_us):
return round(float(2) ** 32 * freq_mhz_per_us / (DAC_Fs_MHZ ** 2))
@portable
def shuttler_freq_sweep(start_f_MHz, end_f_MHz, time_us):
return shuttler_chirp_rate_mu((end_f_MHz - start_f_MHz)/(time_us))
@portable
def shuttler_volt_amp_mu(volt):
return shuttler_volt_to_mu(volt)
@portable
def shuttler_volt_damp_mu(volt_per_us):
return round(float(2) ** 32 * (volt_per_us / 20) / DAC_Fs_MHZ)
@portable
def shuttler_volt_ddamp_mu(volt_per_us_square):
return round(float(2) ** 48 * (volt_per_us_square / 20) * 2 / (DAC_Fs_MHZ ** 2))
@portable
def shuttler_volt_dddamp_mu(volt_per_us_cube):
return round(float(2) ** 48 * (volt_per_us_cube / 20) * 6 / (DAC_Fs_MHZ ** 3))
@portable
def shuttler_dds_amp_mu(volt):
return shuttler_volt_amp_mu(volt / CORDIC_GAIN)
@portable
def shuttler_dds_damp_mu(volt_per_us):
return shuttler_volt_damp_mu(volt_per_us / CORDIC_GAIN)
@portable
def shuttler_dds_ddamp_mu(volt_per_us_square):
return shuttler_volt_ddamp_mu(volt_per_us_square / CORDIC_GAIN)
@portable
def shuttler_dds_dddamp_mu(volt_per_us_cube):
return shuttler_volt_dddamp_mu(volt_per_us_cube / CORDIC_GAIN)
class Shuttler(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("core_dma")
self.setattr_device("scheduler")
self.shuttler0_leds = [ self.get_device("shuttler0_led{}".format(i)) for i in range(2) ]
self.setattr_device("shuttler0_config")
self.setattr_device("shuttler0_trigger")
self.shuttler0_dcbias = [ self.get_device("shuttler0_dcbias{}".format(i)) for i in range(16) ]
self.shuttler0_dds = [ self.get_device("shuttler0_dds{}".format(i)) for i in range(16) ]
self.setattr_device("shuttler0_relay")
self.setattr_device("shuttler0_adc")
@kernel
def record(self):
with self.core_dma.record("example_waveform"):
self.example_waveform()
@kernel
def init(self):
self.led()
self.relay_init()
self.adc_init()
self.shuttler_reset()
@kernel
def run(self):
self.core.reset()
self.core.break_realtime()
self.init()
self.record()
example_waveform_handle = self.core_dma.get_handle("example_waveform")
print("Example Waveforms are on OUT0 and OUT1")
self.core.break_realtime()
while not(self.scheduler.check_termination()):
delay(1*s)
self.core_dma.playback_handle(example_waveform_handle)
@kernel
def shuttler_reset(self):
for i in range(16):
self.shuttler_channel_reset(i)
# To avoid RTIO Underflow
delay(50*us)
@kernel
def shuttler_channel_reset(self, ch):
self.shuttler0_dcbias[ch].set_waveform(
a0=0,
a1=0,
a2=0,
a3=0,
)
self.shuttler0_dds[ch].set_waveform(
b0=0,
b1=0,
b2=0,
b3=0,
c0=0,
c1=0,
c2=0,
)
self.shuttler0_trigger.trigger(1 << ch)
@kernel
def example_waveform(self):
# Equation of Output Waveform
# w(t_us) = a(t_us) + b(t_us) * cos(c(t_us))
# Step 1:
# Enable the Output Relay of OUT0 and OUT1
# Step 2: Cosine Wave Frequency Sweep from 10kHz to 50kHz in 500us
# OUT0: b(t_us) = 1
# c(t_us) = 2 * pi * (0.08 * t_us ^ 2 + 0.01 * t_us)
# OUT1: b(t_us) = 1
# c(t_us) = 2 * pi * (0.05 * t_us)
# Step 3(after 500us): Cosine Wave with 180 Degree Phase Offset
# OUT0: b(t_us) = 1
# c(t_us) = 2 * pi * (0.05 * t_us) + pi
# OUT1: b(t_us) = 1
# c(t_us) = 2 * pi * (0.05 * t_us)
# Step 4(after 500us): Cosine Wave with Amplitude Envelop
# OUT0: b(t_us) = -0.0001367187 * t_us ^ 2 + 0.06835937 * t_us
# c(t_us) = 2 * pi * (0.05 * t_us)
# OUT1: b(t_us) = -0.0001367187 * t_us ^ 2 + 0.06835937 * t_us
# c(t_us) = 0
# Step 5(after 500us): Sawtooth Wave Modulated with 50kHz Cosine Wave
# OUT0: a(t_us) = 0.01 * t_us - 5
# b(t_us) = 1
# c(t_us) = 2 * pi * (0.05 * t_us)
# OUT1: a(t_us) = 0.01 * t_us - 5
# Step 6(after 1000us): A Combination of Previous Waveforms
# OUT0: a(t_us) = 0.01 * t_us - 5
# b(t_us) = -0.0001367187 * t_us ^ 2 + 0.06835937 * t_us
# c(t_us) = 2 * pi * (0.08 * t_us ^ 2 + 0.01 * t_us)
# Step 7(after 500us): Mirrored Waveform in Step 6
# OUT0: a(t_us) = 2.5 + -0.01 * (1000 ^ 2) * t_us
# b(t_us) = 0.0001367187 * t_us ^ 2 - 0.06835937 * t_us
# c(t_us) = 2 * pi * (-0.08 * t_us ^ 2 + 0.05 * t_us) + pi
# Step 8(after 500us):
# Disable Output Relay of OUT0 and OUT1
# Reset OUT0 and OUT1
## Step 1 ##
self.shuttler0_relay.enable(0b11)
## Step 2 ##
start_f_MHz = 0.01
end_f_MHz = 0.05
duration_us = 500
# OUT0 and OUT1 have their frequency and phase aligned at 500us
self.shuttler0_dds[0].set_waveform(
b0=shuttler_dds_amp_mu(1.0),
b1=0,
b2=0,
b3=0,
c0=0,
c1=shuttler_freq_mu(start_f_MHz),
c2=shuttler_freq_sweep(start_f_MHz, end_f_MHz, duration_us),
)
self.shuttler0_dds[1].set_waveform(
b0=shuttler_dds_amp_mu(1.0),
b1=0,
b2=0,
b3=0,
c0=0,
c1=shuttler_freq_mu(end_f_MHz),
c2=0,
)
self.shuttler0_trigger.trigger(0b11)
delay(500*us)
## Step 3 ##
# OUT0 and OUT1 has 180 degree phase difference
self.shuttler0_dds[0].set_waveform(
b0=shuttler_dds_amp_mu(1.0),
b1=0,
b2=0,
b3=0,
c0=shuttler_phase_offset(180.0),
c1=shuttler_freq_mu(end_f_MHz),
c2=0,
)
# Phase and Output Setting of OUT1 is retained
# if the channel is not triggered or config is not cleared
self.shuttler0_trigger.trigger(0b1)
delay(500*us)
## Step 4 ##
# b(0) = 0, b(250) = 8.545, b(500) = 0
self.shuttler0_dds[0].set_waveform(
b0=0,
b1=shuttler_dds_damp_mu(0.06835937),
b2=shuttler_dds_ddamp_mu(-0.0001367187),
b3=0,
c0=0,
c1=shuttler_freq_mu(end_f_MHz),
c2=0,
)
self.shuttler0_dds[1].set_waveform(
b0=0,
b1=shuttler_dds_damp_mu(0.06835937),
b2=shuttler_dds_ddamp_mu(-0.0001367187),
b3=0,
c0=0,
c1=0,
c2=0,
)
self.shuttler0_trigger.trigger(0b11)
delay(500*us)
## Step 5 ##
self.shuttler0_dcbias[0].set_waveform(
a0=shuttler_volt_amp_mu(-5.0),
a1=int32(shuttler_volt_damp_mu(0.01)),
a2=0,
a3=0,
)
self.shuttler0_dds[0].set_waveform(
b0=shuttler_dds_amp_mu(1.0),
b1=0,
b2=0,
b3=0,
c0=0,
c1=shuttler_freq_mu(end_f_MHz),
c2=0,
)
self.shuttler0_dcbias[1].set_waveform(
a0=shuttler_volt_amp_mu(-5.0),
a1=int32(shuttler_volt_damp_mu(0.01)),
a2=0,
a3=0,
)
self.shuttler0_dds[1].set_waveform(
b0=0,
b1=0,
b2=0,
b3=0,
c0=0,
c1=0,
c2=0,
)
self.shuttler0_trigger.trigger(0b11)
delay(1000*us)
## Step 6 ##
self.shuttler0_dcbias[0].set_waveform(
a0=shuttler_volt_amp_mu(-2.5),
a1=int32(shuttler_volt_damp_mu(0.01)),
a2=0,
a3=0,
)
self.shuttler0_dds[0].set_waveform(
b0=0,
b1=shuttler_dds_damp_mu(0.06835937),
b2=shuttler_dds_ddamp_mu(-0.0001367187),
b3=0,
c0=0,
c1=shuttler_freq_mu(start_f_MHz),
c2=shuttler_freq_sweep(start_f_MHz, end_f_MHz, duration_us),
)
self.shuttler0_trigger.trigger(0b1)
self.shuttler_channel_reset(1)
delay(500*us)
## Step 7 ##
self.shuttler0_dcbias[0].set_waveform(
a0=shuttler_volt_amp_mu(2.5),
a1=int32(shuttler_volt_damp_mu(-0.01)),
a2=0,
a3=0,
)
self.shuttler0_dds[0].set_waveform(
b0=0,
b1=shuttler_dds_damp_mu(-0.06835937),
b2=shuttler_dds_ddamp_mu(0.0001367187),
b3=0,
c0=shuttler_phase_offset(180.0),
c1=shuttler_freq_mu(end_f_MHz),
c2=shuttler_freq_sweep(end_f_MHz, start_f_MHz, duration_us),
)
self.shuttler0_trigger.trigger(0b1)
delay(500*us)
## Step 8 ##
self.shuttler0_relay.enable(0)
self.shuttler_channel_reset(0)
self.shuttler_channel_reset(1)
@kernel
def led(self):
for i in range(2):
for j in range(3):
self.shuttler0_leds[i].pulse(.1*s)
delay(.1*s)
@kernel
def relay_init(self):
self.shuttler0_relay.init()
self.shuttler0_relay.enable(0x0000)
@kernel
def adc_init(self):
delay_mu(int64(self.core.ref_multiplier))
self.shuttler0_adc.power_up()
delay_mu(int64(self.core.ref_multiplier))
assert self.shuttler0_adc.read_id() >> 4 == 0x038d
delay_mu(int64(self.core.ref_multiplier))
# The actual output voltage is limited by the hardware, the calculated calibration gain and offset.
# For example, if the system has a calibration gain of 1.06, then the max output voltage = 10 / 1.06 = 9.43V.
# Setting a value larger than 9.43V will result in overflow.
self.shuttler0_adc.calibrate(self.shuttler0_dcbias, self.shuttler0_trigger, self.shuttler0_config)

View File

@ -5,11 +5,7 @@ device_db = {
"type": "local",
"module": "artiq.coredevice.core",
"class": "Core",
"arguments": {
"host": core_addr,
"ref_period": 1e-9,
"analyzer_proxy": "core_analyzer"
}
"arguments": {"host": core_addr, "ref_period": 1e-9}
},
"core_log": {
"type": "controller",
@ -24,13 +20,6 @@ device_db = {
"port": 1384,
"command": "aqctl_moninj_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr
},
"core_analyzer": {
"type": "controller",
"host": "::1",
"port_proxy": 1385,
"port": 1386,
"command": "aqctl_coreanalyzer_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr
},
"core_cache": {
"type": "local",
"module": "artiq.coredevice.cache",

View File

@ -9,11 +9,7 @@ device_db = {
"type": "local",
"module": "artiq.coredevice.core",
"class": "Core",
"arguments": {
"host": core_addr,
"ref_period": 1e-9,
"analyzer_proxy": "core_analyzer"
}
"arguments": {"host": core_addr, "ref_period": 1e-9}
},
"core_log": {
"type": "controller",
@ -28,13 +24,6 @@ device_db = {
"port": 1384,
"command": "aqctl_moninj_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr
},
"core_analyzer": {
"type": "controller",
"host": "::1",
"port_proxy": 1385,
"port": 1386,
"command": "aqctl_coreanalyzer_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr
},
"core_cache": {
"type": "local",
"module": "artiq.coredevice.cache",

View File

@ -20,7 +20,7 @@ class DDSSetter(EnvExperiment):
"driver": self.get_device(k),
"frequency": self.get_argument(
"{}_frequency".format(k),
NumberValue(100e6, scale=1e6, unit="MHz", precision=6))
NumberValue(100e6, scale=1e6, unit="MHz", ndecimals=6))
}
@kernel

View File

@ -12,8 +12,8 @@ class PhotonHistogram(EnvExperiment):
self.setattr_device("bdd_sw")
self.setattr_device("pmt")
self.setattr_argument("nbins", NumberValue(100, precision=0, step=1))
self.setattr_argument("repeats", NumberValue(100, precision=0, step=1))
self.setattr_argument("nbins", NumberValue(100, ndecimals=0, step=1))
self.setattr_argument("repeats", NumberValue(100, ndecimals=0, step=1))
self.setattr_dataset("cool_f", 230*MHz)
self.setattr_dataset("detect_f", 220*MHz)

View File

@ -79,7 +79,7 @@ class SpeedBenchmark(EnvExperiment):
"CoreSend1MB",
"CorePrimes"]))
self.setattr_argument("nruns", NumberValue(10, min=1, max=1000,
precision=0, step=1))
ndecimals=0, step=1))
self.setattr_device("core")
self.setattr_device("scheduler")

View File

@ -0,0 +1,102 @@
core_addr = "192.168.1.65"
device_db = {
"core": {
"type": "local",
"module": "artiq.coredevice.core",
"class": "Core",
"arguments": {"host": core_addr, "ref_period": 1/(8*150e6)}
},
"core_log": {
"type": "controller",
"host": "::1",
"port": 1068,
"command": "aqctl_corelog -p {port} --bind {bind} " + core_addr
},
"core_moninj": {
"type": "controller",
"host": "::1",
"port_proxy": 1383,
"port": 1384,
"command": "aqctl_moninj_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr
},
"core_cache": {
"type": "local",
"module": "artiq.coredevice.cache",
"class": "CoreCache"
},
"core_dma": {
"type": "local",
"module": "artiq.coredevice.dma",
"class": "CoreDMA"
}
}
# master peripherals
for i in range(4):
device_db["led" + str(i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": i},
}
# DEST#1 peripherals
amc_base = 0x070000
rtm_base = 0x020000
for i in range(4):
device_db["led" + str(4+i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": amc_base + i},
}
#DIO (EEM0) starting at RTIO channel 0x000056
for i in range(8):
device_db["ttl" + str(i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": amc_base + 0x000056 + i},
}
#DIO (EEM1) starting at RTIO channel 0x00005e
for i in range(8):
device_db["ttl" + str(8+i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": amc_base + 0x00005e + i},
}
device_db["fmcdio_dirctl_clk"] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": amc_base + 0x000066}
}
device_db["fmcdio_dirctl_ser"] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": amc_base + 0x000067}
}
device_db["fmcdio_dirctl_latch"] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": amc_base + 0x000068}
}
device_db["fmcdio_dirctl"] = {
"type": "local",
"module": "artiq.coredevice.shiftreg",
"class": "ShiftReg",
"arguments": {"clk": "fmcdio_dirctl_clk",
"ser": "fmcdio_dirctl_ser",
"latch": "fmcdio_dirctl_latch"}
}

View File

@ -0,0 +1,129 @@
import sys
import os
import select
from artiq.experiment import *
from artiq.coredevice.fmcdio_vhdci_eem import *
def chunker(seq, size):
res = []
for el in seq:
res.append(el)
if len(res) == size:
yield res
res = []
if res:
yield res
def is_enter_pressed() -> TBool:
if os.name == "nt":
if msvcrt.kbhit() and msvcrt.getch() == b"\r":
return True
else:
return False
else:
if select.select([sys.stdin, ], [], [], 0.0)[0]:
sys.stdin.read(1)
return True
else:
return False
class Demo(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("fmcdio_dirctl")
self.leds = dict()
self.ttl_outs = dict()
ddb = self.get_device_db()
for name, desc in ddb.items():
if isinstance(desc, dict) and desc["type"] == "local":
module, cls = desc["module"], desc["class"]
if (module, cls) == ("artiq.coredevice.ttl", "TTLOut"):
dev = self.get_device(name)
if "led" in name: # guess
self.leds[name] = dev
elif "ttl" in name: # to exclude fmcdio_dirctl
self.ttl_outs[name] = dev
self.leds = sorted(self.leds.items(), key=lambda x: x[1].channel)
self.ttl_outs = sorted(self.ttl_outs.items(), key=lambda x: x[1].channel)
self.dirctl_word = (
shiftreg_bits(0, dio_bank0_out_pins | dio_bank1_out_pins) |
shiftreg_bits(1, dio_bank0_out_pins | dio_bank1_out_pins)
)
@kernel
def init(self):
self.core.break_realtime()
print("*** Waiting for DRTIO ready...")
drtio_indices = [7]
for i in drtio_indices:
while not self.drtio_is_up(i):
pass
self.fmcdio_dirctl.set(self.dirctl_word)
@kernel
def drtio_is_up(self, drtio_index):
if not self.core.get_rtio_destination_status(drtio_index):
return False
print("DRTIO #", drtio_index, "is ready\n")
return True
@kernel
def test_led(self, led):
while not is_enter_pressed():
self.core.break_realtime()
# do not fill the FIFOs too much to avoid long response times
t = now_mu() - self.core.seconds_to_mu(0.2)
while self.core.get_rtio_counter_mu() < t:
pass
for i in range(3):
led.pulse(100*ms)
delay(100*ms)
def test_leds(self):
print("*** Testing LEDs.")
print("Check for blinking. Press ENTER when done.")
for led_name, led_dev in self.leds:
print("Testing LED: {}".format(led_name))
self.test_led(led_dev)
@kernel
def test_ttl_out_chunk(self, ttl_chunk):
while not is_enter_pressed():
self.core.break_realtime()
for _ in range(50000):
i = 0
for ttl in ttl_chunk:
i += 1
for _ in range(i):
ttl.pulse(1*us)
delay(1*us)
delay(10*us)
def test_ttl_outs(self):
print("*** Testing TTL outputs.")
print("Outputs are tested in groups of 4. Touch each TTL connector")
print("with the oscilloscope probe tip, and check that the number of")
print("pulses corresponds to its number in the group.")
print("Press ENTER when done.")
for ttl_chunk in chunker(self.ttl_outs, 4):
print("Testing TTL outputs: {}.".format(", ".join(name for name, dev in ttl_chunk)))
self.test_ttl_out_chunk([dev for name, dev in ttl_chunk])
def run(self):
self.core.reset()
if self.leds:
self.test_leds()
if self.ttl_outs:
self.test_ttl_outs()

View File

@ -45,13 +45,13 @@ class ArgumentsDemo(EnvExperiment):
PYONValue(self.get_dataset("foo", default=42)))
self.setattr_argument("number", NumberValue(42e-6,
unit="us",
precision=4))
ndecimals=4))
self.setattr_argument("integer", NumberValue(42,
step=1, precision=0))
step=1, ndecimals=0))
self.setattr_argument("string", StringValue("Hello World"))
self.setattr_argument("scan", Scannable(global_max=400,
default=NoScan(325),
precision=6))
ndecimals=6))
self.setattr_argument("boolean", BooleanValue(True), "Group")
self.setattr_argument("enum", EnumerationValue(
["foo", "bar", "quux"], "foo"), "Group")

View File

@ -4,13 +4,13 @@ from artiq.applets.simple import SimpleApplet
class DemoWidget(QtWidgets.QLabel):
def __init__(self, args, ctl):
def __init__(self, args):
QtWidgets.QLabel.__init__(self)
self.dataset_name = args.dataset
def data_changed(self, value, metadata, persist, mods):
def data_changed(self, data, mods):
try:
n = str(value[self.dataset_name])
n = str(data[self.dataset_name][1])
except (KeyError, ValueError, TypeError):
n = "---"
n = "<font size=15>" + n + "</font>"

View File

@ -0,0 +1,173 @@
core_addr = "192.168.1.60"
device_db = {
"core": {
"type": "local",
"module": "artiq.coredevice.core",
"class": "Core",
"arguments": {"host": core_addr, "ref_period": 1/(8*150e6)}
},
"core_log": {
"type": "controller",
"host": "::1",
"port": 1068,
"command": "aqctl_corelog -p {port} --bind {bind} " + core_addr
},
"core_moninj": {
"type": "controller",
"host": "::1",
"port_proxy": 1383,
"port": 1384,
"command": "aqctl_moninj_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr
},
"core_cache": {
"type": "local",
"module": "artiq.coredevice.cache",
"class": "CoreCache"
},
"core_dma": {
"type": "local",
"module": "artiq.coredevice.dma",
"class": "CoreDMA"
},
}
for i in range(4):
device_db["led" + str(i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": i},
}
for i in range(2):
device_db["ttl" + str(i)] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLInOut",
"arguments": {"channel": 4 + i},
}
device_db.update(
fmcdio_dirctl_clk={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 6}
},
fmcdio_dirctl_ser={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 7}
},
fmcdio_dirctl_latch={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 8}
},
fmcdio_dirctl={
"type": "local",
"module": "artiq.coredevice.shiftreg",
"class": "ShiftReg",
"arguments": {"clk": "fmcdio_dirctl_clk",
"ser": "fmcdio_dirctl_ser",
"latch": "fmcdio_dirctl_latch"}
}
)
device_db.update(
spi_urukul0={
"type": "local",
"module": "artiq.coredevice.spi2",
"class": "SPIMaster",
"arguments": {"channel": 17}
},
ttl_urukul0_io_update={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 18}
},
ttl_urukul0_sw0={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 19}
},
ttl_urukul0_sw1={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 20}
},
ttl_urukul0_sw2={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 21}
},
ttl_urukul0_sw3={
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 22}
},
urukul0_cpld={
"type": "local",
"module": "artiq.coredevice.urukul",
"class": "CPLD",
"arguments": {
"spi_device": "spi_urukul0",
"io_update_device": "ttl_urukul0_io_update",
"refclk": 125e6,
"clk_sel": 0
}
}
)
for i in range(4):
device_db["urukul0_ch" + str(i)] = {
"type": "local",
"module": "artiq.coredevice.ad9910",
"class": "AD9910",
"arguments": {
"pll_n": 32,
"chip_select": 4 + i,
"cpld_device": "urukul0_cpld",
"sw_device": "ttl_urukul0_sw" + str(i)
}
}
device_db["spi_zotino0"] = {
"type": "local",
"module": "artiq.coredevice.spi2",
"class": "SPIMaster",
"arguments": {"channel": 23}
}
device_db["ttl_zotino0_ldac"] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 24}
}
device_db["ttl_zotino0_clr"] = {
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {"channel": 25}
}
device_db["zotino0"] = {
"type": "local",
"module": "artiq.coredevice.zotino",
"class": "Zotino",
"arguments": {
"spi_device": "spi_zotino0",
"ldac_device": "ttl_zotino0_ldac",
"clr_device": "ttl_zotino0_clr"
}
}

View File

@ -0,0 +1,41 @@
from artiq.experiment import *
from artiq.coredevice.fmcdio_vhdci_eem import *
class Demo(EnvExperiment):
def build(self):
self.setattr_device("core")
self.setattr_device("fmcdio_dirctl")
self.ttls = [self.get_device("ttl" + str(i)) for i in range(8)]
self.setattr_device("urukul0_cpld")
self.urukul_chs = [self.get_device("urukul0_ch" + str(i)) for i in range(4)]
self.setattr_device("zotino0")
self.dirctl_word = (
shiftreg_bits(1, urukul_out_pins) |
shiftreg_bits(0, urukul_aux_out_pins) |
shiftreg_bits(2, dio_bank0_out_pins | dio_bank1_out_pins) |
shiftreg_bits(3, zotino_out_pins))
@kernel
def run(self):
self.core.reset()
delay(10*ms)
self.fmcdio_dirctl.set(self.dirctl_word)
delay(10*ms)
self.urukul0_cpld.init()
delay(10*ms)
self.zotino0.init()
delay(1*ms)
for i in range(32):
self.zotino0.write_dac(i, i/4)
delay(1*ms)
while True:
for ttl in self.ttls:
ttl.pulse(100*ms)
for urukul_ch in self.urukul_chs:
urukul_ch.sw.pulse(100*ms)

View File

@ -13,12 +13,6 @@ dependencies = [
name = "alloc_list"
version = "0.0.0"
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "bare-metal"
version = "0.2.5"
@ -246,12 +240,6 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577"
[[package]]
name = "managed"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
[[package]]
name = "memchr"
version = "2.4.1"
@ -326,7 +314,6 @@ dependencies = [
"build_misoc",
"byteorder",
"cslice",
"dyld",
"eh",
"failure",
"failure_derive",
@ -334,11 +321,10 @@ dependencies = [
"io",
"log",
"logger_artiq",
"managed 0.7.2",
"managed",
"proto_artiq",
"riscv",
"smoltcp",
"tar-no-std",
"unwind_backtrace",
]
@ -355,15 +341,10 @@ dependencies = [
name = "satman"
version = "0.0.0"
dependencies = [
"alloc_list",
"board_artiq",
"board_misoc",
"build_misoc",
"cslice",
"eh",
"io",
"log",
"proto_artiq",
"riscv",
]
@ -384,13 +365,13 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "smoltcp"
version = "0.8.2"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee34c1e1bfc7e9206cc0fb8030a90129b4e319ab53856249bb27642cab914fb3"
checksum = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a"
dependencies = [
"bitflags",
"byteorder",
"managed 0.8.0",
"managed",
]
[[package]]
@ -423,16 +404,6 @@ dependencies = [
"syn",
]
[[package]]
name = "tar-no-std"
version = "0.1.8"
source = "git+https://git.m-labs.hk/M-Labs/tar-no-std?rev=2ab6dc5#2ab6dc58e5249c59c4eb03eaf3a119bcdd678d32"
dependencies = [
"arrayvec",
"bitflags",
"log",
]
[[package]]
name = "unicode-xid"
version = "0.0.4"

View File

@ -16,5 +16,5 @@ build_misoc = { path = "../libbuild_misoc" }
byteorder = { version = "1.0", default-features = false }
crc = { version = "1.7", default-features = false }
board_misoc = { path = "../libboard_misoc", features = ["uart_console", "smoltcp"] }
smoltcp = { version = "0.8.2", default-features = false, features = ["medium-ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp"] }
smoltcp = { version = "0.6.0", default-features = false, features = ["ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp"] }
riscv = { version = "0.6.0", features = ["inline-asm"] }

View File

@ -18,10 +18,6 @@ use board_misoc::slave_fpga;
use board_misoc::{clock, ethmac, net_settings};
use board_misoc::uart_console::Console;
use riscv::register::{mcause, mepc, mtval};
#[cfg(has_ethmac)]
use smoltcp::iface::{Routes, SocketStorage};
#[cfg(has_ethmac)]
use smoltcp::wire::{HardwareAddress, IpAddress, Ipv4Address, Ipv6Address};
fn check_integrity() -> bool {
extern {
@ -400,9 +396,6 @@ fn network_boot() {
println!("Initializing network...");
// Assuming only one socket is ever needed by the bootloader.
// The smoltcp reuses the listening socket when the connection is established.
let mut sockets = [SocketStorage::EMPTY];
let mut net_device = unsafe { ethmac::EthernetDevice::new() };
net_device.reset_phy_if_any();
@ -412,38 +405,38 @@ fn network_boot() {
let net_addresses = net_settings::get_adresses();
println!("Network addresses: {}", net_addresses);
let mut ip_addrs = [
IpCidr::new(IpAddress::Ipv4(Ipv4Address::UNSPECIFIED), 0),
net_addresses.ipv6_ll_addr,
IpCidr::new(IpAddress::Ipv6(Ipv6Address::UNSPECIFIED), 0)
IpCidr::new(net_addresses.ipv4_addr, 0),
IpCidr::new(net_addresses.ipv6_ll_addr, 0),
IpCidr::new(net_addresses.ipv6_ll_addr, 0)
];
if let net_settings::Ipv4AddrConfig::Static(ipv4) = net_addresses.ipv4_addr {
ip_addrs[0] = IpCidr::Ipv4(ipv4);
}
if let Some(ipv6) = net_addresses.ipv6_addr {
ip_addrs[2] = IpCidr::Ipv6(ipv6);
let mut interface = match net_addresses.ipv6_addr {
Some(addr) => {
ip_addrs[2] = IpCidr::new(addr, 0);
smoltcp::iface::EthernetInterfaceBuilder::new(net_device)
.ethernet_addr(net_addresses.hardware_addr)
.ip_addrs(&mut ip_addrs[..])
.neighbor_cache(neighbor_cache)
.finalize()
}
None =>
smoltcp::iface::EthernetInterfaceBuilder::new(net_device)
.ethernet_addr(net_addresses.hardware_addr)
.ip_addrs(&mut ip_addrs[..2])
.neighbor_cache(neighbor_cache)
.finalize()
};
let mut routes = [None; 2];
let mut interface = smoltcp::iface::InterfaceBuilder::new(net_device, &mut sockets[..])
.hardware_addr(HardwareAddress::Ethernet(net_addresses.hardware_addr))
.ip_addrs(&mut ip_addrs[..])
.neighbor_cache(neighbor_cache)
.routes(Routes::new(&mut routes[..]))
.finalize();
if let Some(default_route) = net_addresses.ipv4_default_route {
interface.routes_mut().add_default_ipv4_route(default_route).unwrap();
}
if let Some(default_route) = net_addresses.ipv6_default_route {
interface.routes_mut().add_default_ipv6_route(default_route).unwrap();
}
let mut rx_storage = [0; 4096];
let mut tx_storage = [0; 128];
let mut socket_set_entries: [_; 1] = Default::default();
let mut sockets =
smoltcp::socket::SocketSet::new(&mut socket_set_entries[..]);
let tcp_rx_buffer = smoltcp::socket::TcpSocketBuffer::new(&mut rx_storage[..]);
let tcp_tx_buffer = smoltcp::socket::TcpSocketBuffer::new(&mut tx_storage[..]);
let tcp_socket = smoltcp::socket::TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);
let tcp_handle = interface.add_socket(tcp_socket);
let tcp_handle = sockets.add(tcp_socket);
let mut net_conn = NetConn::new();
let mut boot_time = None;
@ -453,7 +446,7 @@ fn network_boot() {
loop {
let timestamp = clock::get_ms() as i64;
{
let socket = &mut *interface.get_socket::<smoltcp::socket::TcpSocket>(tcp_handle);
let socket = &mut *sockets.get::<smoltcp::socket::TcpSocket>(tcp_handle);
match boot_time {
None => {
@ -482,7 +475,7 @@ fn network_boot() {
}
}
match interface.poll(smoltcp::time::Instant::from_millis(timestamp)) {
match interface.poll(&mut sockets, smoltcp::time::Instant::from_millis(timestamp)) {
Ok(_) => (),
Err(smoltcp::Error::Unrecognized) => (),
Err(err) => println!("Network error: {}", err)
@ -500,7 +493,7 @@ pub extern fn main() -> i32 {
println!(r"|_| |_|_|____/ \___/ \____|");
println!("");
println!("MiSoC Bootloader");
println!("Copyright (c) 2017-2023 M-Labs Limited");
println!("Copyright (c) 2017-2022 M-Labs Limited");
println!("");
#[cfg(has_ethmac)]

View File

@ -157,11 +157,6 @@ static mut API: &'static [(&'static str, *const ())] = &[
api!(dma_retrieve = ::dma_retrieve),
api!(dma_playback = ::dma_playback),
api!(subkernel_load_run = ::subkernel_load_run),
api!(subkernel_send_message = ::subkernel_send_message),
api!(subkernel_await_message = ::subkernel_await_message),
api!(subkernel_await_finish = ::subkernel_await_finish),
api!(i2c_start = ::nrt_bus::i2c::start),
api!(i2c_restart = ::nrt_bus::i2c::restart),
api!(i2c_stop = ::nrt_bus::i2c::stop),

View File

@ -333,7 +333,7 @@ extern fn stop_fn(_version: c_int,
}
}
static EXCEPTION_ID_LOOKUP: [(&str, u32); 12] = [
static EXCEPTION_ID_LOOKUP: [(&str, u32); 11] = [
("RuntimeError", 0),
("RTIOUnderflow", 1),
("RTIOOverflow", 2),
@ -345,7 +345,6 @@ static EXCEPTION_ID_LOOKUP: [(&str, u32); 12] = [
("ZeroDivisionError", 8),
("IndexError", 9),
("UnwrapNoneError", 10),
("SubkernelError", 11)
];
pub fn get_exception_id(name: &str) -> u32 {

View File

@ -17,7 +17,7 @@ void send_to_rtio_log(struct slice data);
#define KERNELCPU_EXEC_ADDRESS 0x45000000
#define KERNELCPU_PAYLOAD_ADDRESS 0x45060000
#define KERNELCPU_LAST_ADDRESS 0x4fffffff
#define KSUPPORT_HEADER_SIZE 0x74
#define KSUPPORT_HEADER_SIZE 0x80
FILE *stderr;

View File

@ -35,6 +35,16 @@ SECTIONS
*(.text .text.*)
} :text
/* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */
.got : {
PROVIDE(_GLOBAL_OFFSET_TABLE_ = .);
*(.got)
} :text
.got.plt : {
*(.got.plt)
} :text
.rodata :
{
*(.rodata .rodata.*)

View File

@ -21,6 +21,7 @@ use dyld::Library;
use board_artiq::{mailbox, rpc_queue};
use proto_artiq::{kernel_proto, rpc_proto};
use kernel_proto::*;
#[cfg(has_rtio_dma)]
use board_misoc::csr;
use riscv::register::{mcause, mepc, mtval};
@ -138,7 +139,7 @@ extern fn rpc_send_async(service: u32, tag: &CSlice<u8>, data: *const *const ())
rpc_queue::enqueue(|mut slice| {
let length = {
let mut writer = Cursor::new(&mut slice[4..]);
rpc_proto::send_args(&mut writer, service, tag.as_ref(), data, true)?;
rpc_proto::send_args(&mut writer, service, tag.as_ref(), data)?;
writer.position()
};
io::ProtoWrite::write_u32(&mut slice, length as u32)
@ -268,7 +269,7 @@ extern fn dma_record_start(name: &CSlice<u8>) {
}
#[unwind(allowed)]
extern fn dma_record_stop(duration: i64, enable_ddma: bool) {
extern fn dma_record_stop(duration: i64) {
unsafe {
dma_record_flush();
@ -284,8 +285,7 @@ extern fn dma_record_stop(duration: i64, enable_ddma: bool) {
DMA_RECORDER.active = false;
send(&DmaRecordStop {
duration: duration as u64,
enable_ddma: enable_ddma
duration: duration as u64
});
}
}
@ -371,7 +371,6 @@ extern fn dma_erase(name: &CSlice<u8>) {
struct DmaTrace {
duration: i64,
address: i32,
uses_ddma: bool,
}
#[unwind(allowed)]
@ -379,12 +378,11 @@ extern fn dma_retrieve(name: &CSlice<u8>) -> DmaTrace {
let name = str::from_utf8(name.as_ref()).unwrap();
send(&DmaRetrieveRequest { name: name });
recv!(&DmaRetrieveReply { trace, duration, uses_ddma } => {
recv!(&DmaRetrieveReply { trace, duration } => {
match trace {
Some(bytes) => Ok(DmaTrace {
address: bytes.as_ptr() as i32,
duration: duration as i64,
uses_ddma: uses_ddma,
duration: duration as i64
}),
None => Err(())
}
@ -395,9 +393,9 @@ extern fn dma_retrieve(name: &CSlice<u8>) -> DmaTrace {
})
}
#[cfg(kernel_has_rtio_dma)]
#[cfg(has_rtio_dma)]
#[unwind(allowed)]
extern fn dma_playback(timestamp: i64, ptr: i32, _uses_ddma: bool) {
extern fn dma_playback(timestamp: i64, ptr: i32) {
assert!(ptr % 64 == 0);
unsafe {
@ -406,10 +404,6 @@ extern fn dma_playback(timestamp: i64, ptr: i32, _uses_ddma: bool) {
csr::cri_con::selected_write(1);
csr::rtio_dma::enable_write(1);
#[cfg(has_drtio)]
if _uses_ddma {
send(&DmaStartRemoteRequest { id: ptr as i32, timestamp: timestamp });
}
while csr::rtio_dma::enable_read() != 0 {}
csr::cri_con::selected_write(0);
@ -420,107 +414,22 @@ extern fn dma_playback(timestamp: i64, ptr: i32, _uses_ddma: bool) {
csr::rtio_dma::error_write(1);
if error & 1 != 0 {
raise!("RTIOUnderflow",
"RTIO underflow at channel {rtio_channel_info:0}, {1} mu",
channel as i64, timestamp as i64, 0);
"RTIO underflow at {0} mu, channel {1}",
timestamp as i64, channel as i64, 0);
}
if error & 2 != 0 {
raise!("RTIODestinationUnreachable",
"RTIO destination unreachable, output, at channel {rtio_channel_info:0}, {1} mu",
channel as i64, timestamp as i64, 0);
"RTIO destination unreachable, output, at {0} mu, channel {1}",
timestamp as i64, channel as i64, 0);
}
}
}
#[cfg(has_drtio)]
if _uses_ddma {
send(&DmaAwaitRemoteRequest { id: ptr as i32 });
recv!(&DmaAwaitRemoteReply { timeout, error, channel, timestamp } => {
if timeout {
raise!("DMAError",
"Error running DMA on satellite device, timed out waiting for results");
}
if error & 1 != 0 {
raise!("RTIOUnderflow",
"RTIO underflow at channel {rtio_channel_info:0}, {1} mu",
channel as i64, timestamp as i64, 0);
}
if error & 2 != 0 {
raise!("RTIODestinationUnreachable",
"RTIO destination unreachable, output, at channel {rtio_channel_info:0}, {1} mu",
channel as i64, timestamp as i64, 0);
}
});
}
}
#[cfg(not(kernel_has_rtio_dma))]
#[cfg(not(has_rtio_dma))]
#[unwind(allowed)]
extern fn dma_playback(_timestamp: i64, _ptr: i32, _uses_ddma: bool) {
unimplemented!("not(kernel_has_rtio_dma)")
}
#[unwind(allowed)]
extern fn subkernel_load_run(id: u32, run: bool) {
send(&SubkernelLoadRunRequest { id: id, run: run });
recv!(&SubkernelLoadRunReply { succeeded } => {
if !succeeded {
raise!("SubkernelError",
"Error loading or running the subkernel");
}
});
}
#[unwind(allowed)]
extern fn subkernel_await_finish(id: u32, timeout: u64) {
send(&SubkernelAwaitFinishRequest { id: id, timeout: timeout });
recv!(SubkernelAwaitFinishReply { status } => {
match status {
SubkernelStatus::NoError => (),
SubkernelStatus::IncorrectState => raise!("SubkernelError",
"Subkernel not running"),
SubkernelStatus::Timeout => raise!("SubkernelError",
"Subkernel timed out"),
SubkernelStatus::CommLost => raise!("SubkernelError",
"Lost communication with satellite"),
SubkernelStatus::OtherError => raise!("SubkernelError",
"An error occurred during subkernel operation")
}
})
}
#[unwind(aborts)]
extern fn subkernel_send_message(id: u32, count: u8, tag: &CSlice<u8>, data: *const *const ()) {
send(&SubkernelMsgSend {
id: id,
count: count,
tag: tag.as_ref(),
data: data
});
}
#[unwind(allowed)]
extern fn subkernel_await_message(id: u32, timeout: u64, tags: &CSlice<u8>, min: u8, max: u8) -> u8 {
send(&SubkernelMsgRecvRequest { id: id, timeout: timeout, tags: tags.as_ref() });
recv!(SubkernelMsgRecvReply { status, count } => {
match status {
SubkernelStatus::NoError => {
if count < &min || count > &max {
raise!("SubkernelError",
"Received less or more arguments than expected");
}
*count
}
SubkernelStatus::IncorrectState => raise!("SubkernelError",
"Subkernel not running"),
SubkernelStatus::Timeout => raise!("SubkernelError",
"Subkernel timed out"),
SubkernelStatus::CommLost => raise!("SubkernelError",
"Lost communication with satellite"),
SubkernelStatus::OtherError => raise!("SubkernelError",
"An error occurred during subkernel operation")
}
})
// RpcRecvRequest should be called `count` times after this to receive message data
extern fn dma_playback(_timestamp: i64, _ptr: i32) {
unimplemented!("not(has_rtio_dma)")
}
unsafe fn attribute_writeback(typeinfo: *const ()) {
@ -562,8 +471,6 @@ unsafe fn attribute_writeback(typeinfo: *const ()) {
}
}
static mut STACK_GUARD_BASE: usize = 0x0;
#[no_mangle]
pub unsafe fn main() {
eh_artiq::reset_exception_buffer(KERNELCPU_PAYLOAD_ADDRESS);
@ -595,7 +502,6 @@ pub unsafe fn main() {
ptr::write_bytes(__bss_start as *mut u8, 0, (_end - __bss_start) as usize);
board_misoc::pmp::init_stack_guard(_sstack_guard as usize);
STACK_GUARD_BASE = _sstack_guard as usize;
board_misoc::cache::flush_cpu_dcache();
board_misoc::cache::flush_cpu_icache();
@ -625,20 +531,10 @@ pub unsafe fn main() {
#[no_mangle]
#[unwind(allowed)]
pub unsafe extern fn exception(_regs: *const u32) {
pub extern fn exception(_regs: *const u32) {
let pc = mepc::read();
let cause = mcause::read().cause();
let mtval = mtval::read();
if let mcause::Trap::Exception(mcause::Exception::LoadFault)
| mcause::Trap::Exception(mcause::Exception::StoreFault) = cause
{
if mtval >= STACK_GUARD_BASE
&& mtval < (STACK_GUARD_BASE + board_misoc::pmp::STACK_GUARD_SIZE)
{
panic!("{:?} at PC {:#08x} in stack guard page ({:#08x}); stack overflow in user kernel code?",
cause, u32::try_from(pc).unwrap(), mtval);
}
}
panic!("{:?} at PC {:#08x}, trap value {:#08x}", cause, u32::try_from(pc).unwrap(), mtval);
}

View File

@ -67,13 +67,13 @@ mod imp {
}
if status & RTIO_O_STATUS_UNDERFLOW != 0 {
raise!("RTIOUnderflow",
"RTIO underflow at channel {rtio_channel_info:0}, {1} mu, slack {2} mu",
channel as i64, timestamp, timestamp - get_counter());
"RTIO underflow at {0} mu, channel {1}, slack {2} mu",
timestamp, channel as i64, timestamp - get_counter());
}
if status & RTIO_O_STATUS_DESTINATION_UNREACHABLE != 0 {
raise!("RTIODestinationUnreachable",
"RTIO destination unreachable, output, at channel {rtio_channel_info:0}, {1} mu",
channel as i64, timestamp, 0);
"RTIO destination unreachable, output, at {0} mu, channel {1}",
timestamp, channel as i64, 0);
}
}
@ -115,7 +115,7 @@ mod imp {
if status & RTIO_I_STATUS_OVERFLOW != 0 {
raise!("RTIOOverflow",
"RTIO input overflow on channel {rtio_channel_info:0}",
"RTIO input overflow on channel {0}",
channel as i64, 0, 0);
}
if status & RTIO_I_STATUS_WAIT_EVENT != 0 {
@ -123,7 +123,7 @@ mod imp {
}
if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 {
raise!("RTIODestinationUnreachable",
"RTIO destination unreachable, input, on channel {rtio_channel_info:0}",
"RTIO destination unreachable, input, on channel {0}",
channel as i64, 0, 0);
}
@ -143,12 +143,12 @@ mod imp {
if status & RTIO_I_STATUS_OVERFLOW != 0 {
raise!("RTIOOverflow",
"RTIO input overflow on channel {rtio_channel_info:0}",
"RTIO input overflow on channel {0}",
channel as i64, 0, 0);
}
if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 {
raise!("RTIODestinationUnreachable",
"RTIO destination unreachable, input, on channel {rtio_channel_info:0}",
"RTIO destination unreachable, input, on channel {0}",
channel as i64, 0, 0);
}
@ -168,7 +168,7 @@ mod imp {
if status & RTIO_I_STATUS_OVERFLOW != 0 {
raise!("RTIOOverflow",
"RTIO input overflow on channel {rtio_channel_info:0}",
"RTIO input overflow on channel {0}",
channel as i64, 0, 0);
}
if status & RTIO_I_STATUS_WAIT_EVENT != 0 {
@ -176,7 +176,7 @@ mod imp {
}
if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 {
raise!("RTIODestinationUnreachable",
"RTIO destination unreachable, input, on channel {rtio_channel_info:0}",
"RTIO destination unreachable, input, on channel {0}",
channel as i64, 0, 0);
}

View File

@ -24,4 +24,3 @@ proto_artiq = { path = "../libproto_artiq" }
[features]
uart_console = []
alloc = []

View File

@ -1,77 +0,0 @@
use spi;
use board_misoc::{csr, clock};
const DATA_CTRL_REG : u8 = 0x02;
const IRCML_REG : u8 = 0x05;
const QRCML_REG : u8 = 0x08;
const CLKMODE_REG : u8 = 0x14;
const VERSION_REG : u8 = 0x1F;
const RETIMER_CLK_PHASE : u8 = 0b11;
fn hard_reset() {
unsafe {
// Min Pulse Width: 50ns
csr::dac_rst::out_write(1);
clock::spin_us(1);
csr::dac_rst::out_write(0);
}
}
fn spi_setup(dac_sel: u8, half_duplex: bool, end: bool) -> Result<(), &'static str> {
// Clear the cs_polarity and cs config
spi::set_config(0, 0, 8, 64, 0b1111)?;
spi::set_config(0, 1 << 3, 8, 64, (7 - dac_sel) << 1)?;
spi::set_config(0, (half_duplex as u8) << 7 | (end as u8) << 1, 8, 64, 0b0001)?;
Ok(())
}
fn write(dac_sel: u8, reg: u8, val: u8) -> Result<(), &'static str> {
spi_setup(dac_sel, false, false)?;
spi::write(0, (reg as u32) << 24)?;
spi_setup(dac_sel, false, true)?;
spi::write(0, (val as u32) << 24)?;
Ok(())
}
fn read(dac_sel: u8, reg: u8) -> Result<u8, &'static str> {
spi_setup(dac_sel, false, false)?;
spi::write(0, ((reg | 1 << 7) as u32) << 24)?;
spi_setup(dac_sel, true, true)?;
spi::write(0, 0)?;
Ok(spi::read(0)? as u8)
}
pub fn init() -> Result<(), &'static str> {
hard_reset();
for channel in 0..8 {
let reg = read(channel, VERSION_REG)?;
if reg != 0x0A {
debug!("DAC AD9117 Channel {} has incorrect hardware version. VERSION reg: {:02x}", channel, reg);
return Err("DAC AD9117 hardware version is not equal to 0x0A");
}
// Check for the presence of DCLKIO and CLKIN
let reg = read(channel, CLKMODE_REG)?;
if reg >> 4 & 1 != 0 {
debug!("DAC AD9117 Channel {} retiming fails. CLKMODE reg: {:02x}", channel, reg);
return Err("DAC AD9117 retiming failure");
}
// Force RETIMER-CLK to be Phase 1 as DCLKIO and CLKIN is known to be safe at Phase 1
// See Issue #2200
write(channel, CLKMODE_REG, RETIMER_CLK_PHASE << 6 | 1 << 2 | RETIMER_CLK_PHASE)?;
// Set the DACs input data format to be twos complement
// Set IFIRST and IRISING to True
write(channel, DATA_CTRL_REG, 1 << 7 | 1 << 5 | 1 << 4)?;
// Enable internal common mode resistors of both channels
write(channel, IRCML_REG, 1 << 7)?;
write(channel, QRCML_REG, 1 << 7)?;
}
Ok(())
}

View File

@ -0,0 +1,549 @@
use board_misoc::{csr, clock};
use ad9154_reg;
fn spi_setup(dacno: u8) {
unsafe {
while csr::converter_spi::idle_read() == 0 {}
csr::converter_spi::offline_write(0);
csr::converter_spi::end_write(1);
csr::converter_spi::cs_polarity_write(0b0001);
csr::converter_spi::clk_polarity_write(0);
csr::converter_spi::clk_phase_write(0);
csr::converter_spi::lsb_first_write(0);
csr::converter_spi::half_duplex_write(0);
csr::converter_spi::length_write(24 - 1);
csr::converter_spi::div_write(16 - 2);
csr::converter_spi::cs_write(1 << (csr::CONFIG_CONVERTER_SPI_FIRST_AD9154_CS + dacno as u32));
}
}
fn write(addr: u16, data: u8) {
unsafe {
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::data_write(
((addr as u32) << 16) | ((data as u32) << 8));
}
}
fn read(addr: u16) -> u8 {
unsafe {
write((1 << 15) | addr, 0);
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::data_read() as u8
}
}
// ad9154 mode 1
// linerate 5Gbps or 6Gbps
// deviceclock_fpga 125MHz or 150MHz
// deviceclock_dac 500MHz or 600MHz
struct JESDSettings {
did: u8,
bid: u8,
l: u8, // lanes
m: u8, // converters
n: u8, // bits/converter
np: u8, // bits/sample
f: u8, // octets/(lane and frame)
s: u8, // samples/(converter and frame)
k: u8, // frames/multiframe
cs: u8, // control bits/sample
subclassv: u8,
jesdv: u8
}
fn jesd_checksum(settings: &JESDSettings) -> u8 {
let mut r: u8 = 0;
for field in [
settings.did,
settings.bid,
settings.l - 1,
settings.f - 1,
settings.k - 1,
settings.m - 1,
settings.n - 1,
settings.cs,
settings.np - 1,
settings.subclassv,
settings.s - 1,
settings.jesdv,
].iter() {
r = r.overflowing_add(*field).0;
}
r
}
const JESD_SETTINGS: JESDSettings = JESDSettings {
did: 0x5a,
bid: 0x5,
l: 8,
m: 4,
n: 16,
np: 16,
f: 2,
s: 2,
k: 16,
cs: 0,
subclassv: 1,
jesdv: 1
};
pub fn reset_and_detect(dacno: u8) -> Result<(), &'static str> {
spi_setup(dacno);
// reset
write(ad9154_reg::SPI_INTFCONFA,
1*ad9154_reg::SOFTRESET_M | 1*ad9154_reg::SOFTRESET |
0*ad9154_reg::LSBFIRST_M | 0*ad9154_reg::LSBFIRST |
0*ad9154_reg::ADDRINC_M | 0*ad9154_reg::ADDRINC |
1*ad9154_reg::SDOACTIVE_M | 1*ad9154_reg::SDOACTIVE);
clock::spin_us(100);
write(ad9154_reg::SPI_INTFCONFA,
0*ad9154_reg::SOFTRESET_M | 0*ad9154_reg::SOFTRESET |
0*ad9154_reg::LSBFIRST_M | 0*ad9154_reg::LSBFIRST |
0*ad9154_reg::ADDRINC_M | 0*ad9154_reg::ADDRINC |
1*ad9154_reg::SDOACTIVE_M | 1*ad9154_reg::SDOACTIVE);
clock::spin_us(100);
if (read(ad9154_reg::PRODIDH) as u16) << 8 | (read(ad9154_reg::PRODIDL) as u16) != 0x9154 {
return Err("invalid AD9154 identification");
} else {
info!("AD9154-{} found", dacno);
}
Ok(())
}
pub fn setup(dacno: u8, linerate: u64) -> Result<(), &'static str> {
spi_setup(dacno);
info!("AD9154-{} initializing...", dacno);
write(ad9154_reg::PWRCNTRL0,
0*ad9154_reg::PD_DAC0 | 0*ad9154_reg::PD_DAC1 |
0*ad9154_reg::PD_DAC2 | 0*ad9154_reg::PD_DAC3 |
0*ad9154_reg::PD_BG);
clock::spin_us(100);
write(ad9154_reg::TXENMASK1, 0*ad9154_reg::DACA_MASK |
0*ad9154_reg::DACB_MASK); // DAC PD not controlled by TXEN pins
write(ad9154_reg::PWRCNTRL3, 1*ad9154_reg::ENA_SPI_TXEN |
1*ad9154_reg::SPI_TXEN);
write(ad9154_reg::CLKCFG0,
0*ad9154_reg::REF_CLKDIV_EN | 1*ad9154_reg::RF_SYNC_EN |
1*ad9154_reg::DUTY_EN | 0*ad9154_reg::PD_CLK_REC |
0*ad9154_reg::PD_SERDES_PCLK | 0*ad9154_reg::PD_CLK_DIG |
0*ad9154_reg::PD_CLK23 | 0*ad9154_reg::PD_CLK01);
write(ad9154_reg::DACPLLCNTRL,
0*ad9154_reg::ENABLE_DACPLL | 0*ad9154_reg::RECAL_DACPLL);
write(ad9154_reg::SYSREF_ACTRL0, // jesd204b subclass 1
0*ad9154_reg::HYS_CNTRL1 | 0*ad9154_reg::SYSREF_RISE |
0*ad9154_reg::HYS_ON | 0*ad9154_reg::PD_SYSREF_BUFFER);
write(ad9154_reg::DEVICE_CONFIG_REG_0, 0x8b); // magic
write(ad9154_reg::DEVICE_CONFIG_REG_1, 0x01); // magic
write(ad9154_reg::DEVICE_CONFIG_REG_2, 0x01); // magic
write(ad9154_reg::SPI_PAGEINDX, 0x3); // A and B dual
write(ad9154_reg::INTERP_MODE, 0x03); // 4x
write(ad9154_reg::MIX_MODE, 0);
write(ad9154_reg::DATA_FORMAT, 0*ad9154_reg::BINARY_FORMAT); // s16
write(ad9154_reg::DATAPATH_CTRL,
0*ad9154_reg::I_TO_Q | 0*ad9154_reg::SEL_SIDEBAND |
0*ad9154_reg::MODULATION_TYPE | 0*ad9154_reg::PHASE_ADJ_ENABLE |
1*ad9154_reg::DIG_GAIN_ENABLE | 0*ad9154_reg::INVSINC_ENABLE);
write(ad9154_reg::IDAC_DIG_GAIN0, 0x00);
write(ad9154_reg::IDAC_DIG_GAIN1, 0x8);
write(ad9154_reg::QDAC_DIG_GAIN0, 0x00);
write(ad9154_reg::QDAC_DIG_GAIN1, 0x8);
write(ad9154_reg::DC_OFFSET_CTRL, 0);
write(ad9154_reg::IPATH_DC_OFFSET_1PART0, 0x00);
write(ad9154_reg::IPATH_DC_OFFSET_1PART1, 0x00);
write(ad9154_reg::IPATH_DC_OFFSET_2PART, 0x00);
write(ad9154_reg::QPATH_DC_OFFSET_1PART0, 0x00);
write(ad9154_reg::QPATH_DC_OFFSET_1PART1, 0x00);
write(ad9154_reg::QPATH_DC_OFFSET_2PART, 0x00);
write(ad9154_reg::PHASE_ADJ0, 0);
write(ad9154_reg::PHASE_ADJ1, 0);
write(ad9154_reg::GROUP_DLY, 0x8*ad9154_reg::COARSE_GROUP_DELAY |
0x8*ad9154_reg::GROUP_DELAY_RESERVED);
write(ad9154_reg::GROUPDELAY_COMP_BYP,
1*ad9154_reg::GROUPCOMP_BYPQ |
1*ad9154_reg::GROUPCOMP_BYPI);
write(ad9154_reg::GROUPDELAY_COMP_I, 0);
write(ad9154_reg::GROUPDELAY_COMP_Q, 0);
write(ad9154_reg::PDP_AVG_TIME, 0*ad9154_reg::PDP_ENABLE);
write(ad9154_reg::MASTER_PD, 0);
write(ad9154_reg::PHY_PD, 0x00); // lanes 0-7 enabled
write(ad9154_reg::GENERIC_PD,
0*ad9154_reg::PD_SYNCOUT0B |
1*ad9154_reg::PD_SYNCOUT1B);
write(ad9154_reg::GENERAL_JRX_CTRL_0,
0x0*ad9154_reg::LINK_EN | 0*ad9154_reg::LINK_PAGE |
0*ad9154_reg::LINK_MODE | 0*ad9154_reg::CHECKSUM_MODE);
write(ad9154_reg::ILS_DID, JESD_SETTINGS.did);
write(ad9154_reg::ILS_BID, JESD_SETTINGS.bid);
write(ad9154_reg::ILS_LID0, 0x00); // lane id
write(ad9154_reg::ILS_SCR_L,
(JESD_SETTINGS.l - 1)*ad9154_reg::L_1 |
1*ad9154_reg::SCR);
write(ad9154_reg::ILS_F, JESD_SETTINGS.f - 1);
write(ad9154_reg::ILS_K, JESD_SETTINGS.k - 1);
write(ad9154_reg::ILS_M, JESD_SETTINGS.m - 1);
write(ad9154_reg::ILS_CS_N,
(JESD_SETTINGS.n - 1)*ad9154_reg::N_1 |
0*ad9154_reg::CS);
write(ad9154_reg::ILS_NP,
(JESD_SETTINGS.np - 1)*ad9154_reg::NP_1 |
JESD_SETTINGS.subclassv*ad9154_reg::SUBCLASSV);
write(ad9154_reg::ILS_S,
(JESD_SETTINGS.s - 1)*ad9154_reg::S_1 |
JESD_SETTINGS.jesdv*ad9154_reg::JESDV);
write(ad9154_reg::ILS_HD_CF,
0*ad9154_reg::HD | 0*ad9154_reg::CF);
write(ad9154_reg::ILS_CHECKSUM, jesd_checksum(&JESD_SETTINGS));
write(ad9154_reg::LANEDESKEW, 0xff);
for i in 0..8 {
write(ad9154_reg::BADDISPARITY, 0*ad9154_reg::RST_IRQ_DIS |
0*ad9154_reg::DISABLE_ERR_CNTR_DIS |
1*ad9154_reg::RST_ERR_CNTR_DIS | i*ad9154_reg::LANE_ADDR_DIS);
write(ad9154_reg::BADDISPARITY, 0*ad9154_reg::RST_IRQ_DIS |
0*ad9154_reg::DISABLE_ERR_CNTR_DIS |
0*ad9154_reg::RST_ERR_CNTR_DIS | i*ad9154_reg::LANE_ADDR_DIS);
write(ad9154_reg::NIT_W, 0*ad9154_reg::RST_IRQ_NIT |
0*ad9154_reg::DISABLE_ERR_CNTR_NIT |
1*ad9154_reg::RST_ERR_CNTR_NIT | i*ad9154_reg::LANE_ADDR_NIT);
write(ad9154_reg::NIT_W, 0*ad9154_reg::RST_IRQ_NIT |
0*ad9154_reg::DISABLE_ERR_CNTR_NIT |
0*ad9154_reg::RST_ERR_CNTR_NIT | i*ad9154_reg::LANE_ADDR_NIT);
write(ad9154_reg::UNEXPECTEDCONTROL_W, 0*ad9154_reg::RST_IRQ_UCC |
0*ad9154_reg::DISABLE_ERR_CNTR_UCC |
1*ad9154_reg::RST_ERR_CNTR_UCC | i*ad9154_reg::LANE_ADDR_UCC);
write(ad9154_reg::BADDISPARITY, 0*ad9154_reg::RST_IRQ_UCC |
0*ad9154_reg::DISABLE_ERR_CNTR_UCC |
0*ad9154_reg::RST_ERR_CNTR_UCC | i*ad9154_reg::LANE_ADDR_UCC);
}
write(ad9154_reg::CTRLREG1, JESD_SETTINGS.f);
write(ad9154_reg::CTRLREG2, 0*ad9154_reg::ILAS_MODE |
0*ad9154_reg::THRESHOLD_MASK_EN);
write(ad9154_reg::KVAL, 1); // *4*K multiframes during ILAS
write(ad9154_reg::LANEENABLE, 0xff); // CGS _after_ this
write(ad9154_reg::TERM_BLK1_CTRLREG0, 1);
write(ad9154_reg::TERM_BLK2_CTRLREG0, 1);
write(ad9154_reg::SERDES_SPI_REG, 1);
if linerate > 5_650_000_000 {
write(ad9154_reg::CDR_OPERATING_MODE_REG_0,
0*ad9154_reg::CDR_OVERSAMP | 0x2*ad9154_reg::CDR_RESERVED |
1*ad9154_reg::ENHALFRATE);
} else {
write(ad9154_reg::CDR_OPERATING_MODE_REG_0,
0*ad9154_reg::CDR_OVERSAMP | 0x2*ad9154_reg::CDR_RESERVED |
0*ad9154_reg::ENHALFRATE);
}
write(ad9154_reg::CDR_RESET, 0);
write(ad9154_reg::CDR_RESET, 1);
if linerate > 5_650_000_000 {
write(ad9154_reg::REF_CLK_DIVIDER_LDO,
0*ad9154_reg::SPI_CDR_OVERSAMP |
1*ad9154_reg::SPI_LDO_BYPASS_FILT |
0*ad9154_reg::SPI_LDO_REF_SEL);
} else {
write(ad9154_reg::REF_CLK_DIVIDER_LDO,
1*ad9154_reg::SPI_CDR_OVERSAMP |
1*ad9154_reg::SPI_LDO_BYPASS_FILT |
0*ad9154_reg::SPI_LDO_REF_SEL);
}
write(ad9154_reg::LDO_FILTER_1, 0x62); // magic
write(ad9154_reg::LDO_FILTER_2, 0xc9); // magic
write(ad9154_reg::LDO_FILTER_3, 0x0e); // magic
write(ad9154_reg::CP_CURRENT_SPI,
0x12*ad9154_reg::SPI_CP_CURRENT |
0*ad9154_reg::SPI_SERDES_LOGEN_POWER_MODE);
write(ad9154_reg::VCO_LDO, 0x7b); // magic
write(ad9154_reg::PLL_RD_REG,
0*ad9154_reg::SPI_SERDES_LOGEN_PD_CORE |
0*ad9154_reg::SPI_SERDES_LDO_PD | 0*ad9154_reg::SPI_SYN_PD |
0*ad9154_reg::SPI_VCO_PD_ALC | 0*ad9154_reg::SPI_VCO_PD_PTAT |
0*ad9154_reg::SPI_VCO_PD);
write(ad9154_reg::ALC_VARACTOR,
0x9*ad9154_reg::SPI_VCO_VARACTOR |
0x8*ad9154_reg::SPI_INIT_ALC_VALUE);
write(ad9154_reg::VCO_OUTPUT,
0xc*ad9154_reg::SPI_VCO_OUTPUT_LEVEL |
0x4*ad9154_reg::SPI_VCO_OUTPUT_RESERVED);
write(ad9154_reg::CP_CONFIG,
0*ad9154_reg::SPI_CP_TEST |
1*ad9154_reg::SPI_CP_CAL_EN |
0*ad9154_reg::SPI_CP_FORCE_CALBITS |
0*ad9154_reg::SPI_CP_OFFSET_OFF |
1*ad9154_reg::SPI_CP_ENABLE_MACHINE |
0*ad9154_reg::SPI_CP_DITHER_MODE |
0*ad9154_reg::SPI_CP_HALF_VCO_CAL_CLK);
write(ad9154_reg::VCO_BIAS_1,
0x3*ad9154_reg::SPI_VCO_BIAS_REF |
0x3*ad9154_reg::SPI_VCO_BIAS_TCF);
write(ad9154_reg::VCO_BIAS_2,
0x1*ad9154_reg::SPI_PRESCALE_BIAS |
1*ad9154_reg::SPI_LAST_ALC_EN |
0x1*ad9154_reg::SPI_PRESCALE_BYPASS_R |
0*ad9154_reg::SPI_VCO_COMP_BYPASS_BIASR |
0*ad9154_reg::SPI_VCO_BYPASS_DAC_R);
write(ad9154_reg::VCO_PD_OVERRIDES,
0*ad9154_reg::SPI_VCO_PD_OVERRIDE_VCO_BUF |
1*ad9154_reg::SPI_VCO_PD_OVERRIDE_CAL_TCF |
0*ad9154_reg::SPI_VCO_PD_OVERRIDE_VAR_REF_TCF |
0*ad9154_reg::SPI_VCO_PD_OVERRIDE_VAR_REF);
write(ad9154_reg::VCO_CAL,
0x2*ad9154_reg::SPI_FB_CLOCK_ADV |
0x3*ad9154_reg::SPI_VCO_CAL_COUNT |
0*ad9154_reg::SPI_VCO_CAL_ALC_WAIT |
1*ad9154_reg::SPI_VCO_CAL_EN);
write(ad9154_reg::CP_LEVEL_DETECT,
0x2*ad9154_reg::SPI_CP_LEVEL_THRESHOLD_HIGH |
0x5*ad9154_reg::SPI_CP_LEVEL_THRESHOLD_LOW |
0*ad9154_reg::SPI_CP_LEVEL_DET_PD);
write(ad9154_reg::VCO_VARACTOR_CTRL_0,
0xe*ad9154_reg::SPI_VCO_VARACTOR_OFFSET |
0x7*ad9154_reg::SPI_VCO_VARACTOR_REF_TCF);
write(ad9154_reg::VCO_VARACTOR_CTRL_1,
0x6*ad9154_reg::SPI_VCO_VARACTOR_REF);
// ensure link is txing
//write(ad9154_reg::SERDESPLL_ENABLE_CNTRL,
// 1*ad9154_reg::ENABLE_SERDESPLL | 1*ad9154_reg::RECAL_SERDESPLL)
write(ad9154_reg::SERDESPLL_ENABLE_CNTRL,
1*ad9154_reg::ENABLE_SERDESPLL | 0*ad9154_reg::RECAL_SERDESPLL);
let t = clock::get_ms();
while read(ad9154_reg::PLL_STATUS) & ad9154_reg::SERDES_PLL_LOCK_RB == 0 {
if clock::get_ms() > t + 200 {
return Err("SERDES PLL lock timeout");
}
}
write(ad9154_reg::EQ_BIAS_REG, 0x22*ad9154_reg::EQ_BIAS_RESERVED |
1*ad9154_reg::EQ_POWER_MODE);
write(ad9154_reg::GENERAL_JRX_CTRL_1, 1); // subclass 1
write(ad9154_reg::LMFC_DELAY_0, 0);
write(ad9154_reg::LMFC_DELAY_1, 0);
write(ad9154_reg::LMFC_VAR_0, 0x0a); // receive buffer delay
write(ad9154_reg::LMFC_VAR_1, 0x0a);
write(ad9154_reg::SYNC_ERRWINDOW, 0); // +- 1/2 DAC clock
// datasheet seems to say ENABLE and ARM should be separate steps,
// so enable now so it can be armed in sync().
write(ad9154_reg::SYNC_CONTROL,
0x1*ad9154_reg::SYNCMODE | 1*ad9154_reg::SYNCENABLE |
0*ad9154_reg::SYNCARM | 0*ad9154_reg::SYNCCLRSTKY);
write(ad9154_reg::XBAR_LN_0_1,
0*ad9154_reg::LOGICAL_LANE0_SRC | 1*ad9154_reg::LOGICAL_LANE1_SRC);
write(ad9154_reg::XBAR_LN_2_3,
2*ad9154_reg::LOGICAL_LANE2_SRC | 3*ad9154_reg::LOGICAL_LANE3_SRC);
write(ad9154_reg::XBAR_LN_4_5,
4*ad9154_reg::LOGICAL_LANE4_SRC | 5*ad9154_reg::LOGICAL_LANE5_SRC);
write(ad9154_reg::XBAR_LN_6_7,
6*ad9154_reg::LOGICAL_LANE6_SRC | 7*ad9154_reg::LOGICAL_LANE7_SRC);
write(ad9154_reg::JESD_BIT_INVERSE_CTRL, 0x00);
write(ad9154_reg::GENERAL_JRX_CTRL_0,
0x1*ad9154_reg::LINK_EN | 0*ad9154_reg::LINK_PAGE |
0*ad9154_reg::LINK_MODE | 0*ad9154_reg::CHECKSUM_MODE);
info!(" ...done");
Ok(())
}
pub fn status(dacno: u8) {
spi_setup(dacno);
info!("Printing status of AD9154-{}", dacno);
info!("PRODID: 0x{:04x}", (read(ad9154_reg::PRODIDH) as u16) << 8 | (read(ad9154_reg::PRODIDL) as u16));
info!("SERDES_PLL_LOCK: {}",
(read(ad9154_reg::PLL_STATUS) & ad9154_reg::SERDES_PLL_LOCK_RB));
info!("");
info!("CODEGRPSYNC: 0x{:02x}", read(ad9154_reg::CODEGRPSYNCFLG));
info!("FRAMESYNC: 0x{:02x}", read(ad9154_reg::FRAMESYNCFLG));
info!("GOODCHECKSUM: 0x{:02x}", read(ad9154_reg::GOODCHKSUMFLG));
info!("INITLANESYNC: 0x{:02x}", read(ad9154_reg::INITLANESYNCFLG));
info!("");
info!("DID_REG: 0x{:02x}", read(ad9154_reg::DID_REG));
info!("BID_REG: 0x{:02x}", read(ad9154_reg::BID_REG));
info!("SCR_L_REG: 0x{:02x}", read(ad9154_reg::SCR_L_REG));
info!("F_REG: 0x{:02x}", read(ad9154_reg::F_REG));
info!("K_REG: 0x{:02x}", read(ad9154_reg::K_REG));
info!("M_REG: 0x{:02x}", read(ad9154_reg::M_REG));
info!("CS_N_REG: 0x{:02x}", read(ad9154_reg::CS_N_REG));
info!("NP_REG: 0x{:02x}", read(ad9154_reg::NP_REG));
info!("S_REG: 0x{:02x}", read(ad9154_reg::S_REG));
info!("HD_CF_REG: 0x{:02x}", read(ad9154_reg::HD_CF_REG));
info!("RES1_REG: 0x{:02x}", read(ad9154_reg::RES1_REG));
info!("RES2_REG: 0x{:02x}", read(ad9154_reg::RES2_REG));
info!("LIDx_REG: 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x}",
read(ad9154_reg::LID0_REG),
read(ad9154_reg::LID1_REG),
read(ad9154_reg::LID2_REG),
read(ad9154_reg::LID3_REG),
read(ad9154_reg::LID4_REG),
read(ad9154_reg::LID5_REG),
read(ad9154_reg::LID6_REG),
read(ad9154_reg::LID7_REG));
info!("CHECKSUMx_REG: 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x}",
read(ad9154_reg::CHECKSUM0_REG),
read(ad9154_reg::CHECKSUM1_REG),
read(ad9154_reg::CHECKSUM2_REG),
read(ad9154_reg::CHECKSUM3_REG),
read(ad9154_reg::CHECKSUM4_REG),
read(ad9154_reg::CHECKSUM5_REG),
read(ad9154_reg::CHECKSUM6_REG),
read(ad9154_reg::CHECKSUM7_REG));
info!("COMPSUMx_REG: 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x} 0x{:02x}",
read(ad9154_reg::COMPSUM0_REG),
read(ad9154_reg::COMPSUM1_REG),
read(ad9154_reg::COMPSUM2_REG),
read(ad9154_reg::COMPSUM3_REG),
read(ad9154_reg::COMPSUM4_REG),
read(ad9154_reg::COMPSUM5_REG),
read(ad9154_reg::COMPSUM6_REG),
read(ad9154_reg::COMPSUM7_REG));
info!("BADDISPARITY: 0x{:02x}", read(ad9154_reg::BADDISPARITY));
info!("NITDISPARITY: 0x{:02x}", read(ad9154_reg::NIT_W));
}
pub fn prbs(dacno: u8) -> Result<(), &'static str> {
let mut prbs_errors: u32 = 0;
spi_setup(dacno);
/* follow phy prbs testing (p58 of ad9154 datasheet) */
info!("AD9154-{} running PRBS test...", dacno);
/* step 2: select prbs mode */
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
0b00*ad9154_reg::PHY_PRBS_PAT_SEL);
/* step 3: enable test for all lanes */
write(ad9154_reg::PHY_PRBS_TEST_EN, 0xff);
/* step 4: reset */
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
0b00*ad9154_reg::PHY_PRBS_PAT_SEL |
1*ad9154_reg::PHY_TEST_RESET);
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
0b00*ad9154_reg::PHY_PRBS_PAT_SEL);
/* step 5: prbs threshold */
write(ad9154_reg::PHY_PRBS_TEST_THRESHOLD_LOBITS, 0);
write(ad9154_reg::PHY_PRBS_TEST_THRESHOLD_MIDBITS, 0);
write(ad9154_reg::PHY_PRBS_TEST_THRESHOLD_HIBITS, 0);
/* step 6: start */
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
0b00*ad9154_reg::PHY_PRBS_PAT_SEL);
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
0b00*ad9154_reg::PHY_PRBS_PAT_SEL |
1*ad9154_reg::PHY_TEST_START);
/* step 7: wait 500 ms */
clock::spin_us(500000);
/* step 8 : stop */
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
0b00*ad9154_reg::PHY_PRBS_PAT_SEL);
for i in 0..8 {
/* step 9.a: select src err */
write(ad9154_reg::PHY_PRBS_TEST_CTRL,
i*ad9154_reg::PHY_SRC_ERR_CNT);
/* step 9.b: retrieve number of errors */
let lane_errors = (read(ad9154_reg::PHY_PRBS_TEST_ERRCNT_LOBITS) as u32) |
((read(ad9154_reg::PHY_PRBS_TEST_ERRCNT_MIDBITS) as u32) << 8) |
((read(ad9154_reg::PHY_PRBS_TEST_ERRCNT_HIBITS) as u32) << 16);
if lane_errors > 0 {
warn!(" PRBS errors on lane{}: {:06x}", i, lane_errors);
}
prbs_errors += lane_errors
}
if prbs_errors > 0 {
return Err("PRBS failed")
}
info!(" ...passed");
Ok(())
}
pub fn stpl(dacno: u8, m: u8, s: u8) -> Result<(), &'static str> {
spi_setup(dacno);
info!("AD9154-{} running STPL test...", dacno);
fn prng(seed: u32) -> u32 {
return ((seed + 1)*0x31415979 + 1) & 0xffff;
}
for i in 0..m {
let mut data: u32;
let mut errors: u8 = 0;
for j in 0..s {
/* select converter */
write(ad9154_reg::SHORT_TPL_TEST_0,
0b0*ad9154_reg::SHORT_TPL_TEST_EN |
0b0*ad9154_reg::SHORT_TPL_TEST_RESET |
i*ad9154_reg::SHORT_TPL_DAC_SEL |
j*ad9154_reg::SHORT_TPL_SP_SEL);
/* set expected value */
data = prng(((i as u32) << 8) | (j as u32));
write(ad9154_reg::SHORT_TPL_TEST_1, (data & 0x00ff) as u8);
write(ad9154_reg::SHORT_TPL_TEST_2, ((data & 0xff00) >> 8) as u8);
/* enable stpl */
write(ad9154_reg::SHORT_TPL_TEST_0,
0b1*ad9154_reg::SHORT_TPL_TEST_EN |
0b0*ad9154_reg::SHORT_TPL_TEST_RESET |
i*ad9154_reg::SHORT_TPL_DAC_SEL |
j*ad9154_reg::SHORT_TPL_SP_SEL);
/* reset stpl */
write(ad9154_reg::SHORT_TPL_TEST_0,
0b1*ad9154_reg::SHORT_TPL_TEST_EN |
0b1*ad9154_reg::SHORT_TPL_TEST_RESET |
i*ad9154_reg::SHORT_TPL_DAC_SEL |
j*ad9154_reg::SHORT_TPL_SP_SEL);
/* release reset stpl */
write(ad9154_reg::SHORT_TPL_TEST_0,
0b1*ad9154_reg::SHORT_TPL_TEST_EN |
0b0*ad9154_reg::SHORT_TPL_TEST_RESET |
i*ad9154_reg::SHORT_TPL_DAC_SEL |
j*ad9154_reg::SHORT_TPL_SP_SEL);
errors += read(ad9154_reg::SHORT_TPL_TEST_3);
}
info!(" c{} errors: {}", i, errors);
if errors > 0 {
return Err("STPL failed")
}
}
info!(" ...passed");
Ok(())
}
pub fn sync(dacno: u8) -> Result<bool, &'static str> {
spi_setup(dacno);
write(ad9154_reg::SYNC_CONTROL,
0x1*ad9154_reg::SYNCMODE | 1*ad9154_reg::SYNCENABLE |
1*ad9154_reg::SYNCARM | 1*ad9154_reg::SYNCCLRSTKY);
clock::spin_us(1000); // ensure at least one sysref edge
let sync_status = read(ad9154_reg::SYNC_STATUS);
if sync_status & ad9154_reg::SYNC_BUSY != 0 {
return Err("sync logic busy");
}
if sync_status & ad9154_reg::SYNC_LOCK == 0 {
return Err("no sync lock");
}
if sync_status & ad9154_reg::SYNC_TRIP == 0 {
return Err("no sysref edge");
}
let realign_occured = sync_status & ad9154_reg::SYNC_ROTATE != 0;
Ok(realign_occured)
}

View File

@ -0,0 +1,826 @@
#![allow(dead_code)]
pub const SPI_INTFCONFA : u16 = 0x000;
pub const SOFTRESET : u8 = 1 << 0;
pub const LSBFIRST : u8 = 1 << 1;
pub const ADDRINC : u8 = 1 << 2;
pub const SDOACTIVE : u8 = 1 << 3;
pub const SDOACTIVE_M : u8 = 1 << 4;
pub const ADDRINC_M : u8 = 1 << 5;
pub const LSBFIRST_M : u8 = 1 << 6;
pub const SOFTRESET_M : u8 = 1 << 7;
pub const CHIPTYPE : u16 = 0x003;
pub const PRODIDL : u16 = 0x004;
pub const PRODIDH : u16 = 0x005;
pub const CHIPGRADE : u16 = 0x006;
pub const DEV_REVISION : u8 = 1 << 0;
pub const PROD_GRADE : u8 = 1 << 4;
pub const SPI_PAGEINDX : u16 = 0x008;
pub const PWRCNTRL0 : u16 = 0x011;
pub const PD_DAC3 : u8 = 1 << 3;
pub const PD_DAC2 : u8 = 1 << 4;
pub const PD_DAC1 : u8 = 1 << 5;
pub const PD_DAC0 : u8 = 1 << 6;
pub const PD_BG : u8 = 1 << 7;
pub const TXENMASK1 : u16 = 0x012;
pub const DACA_MASK : u8 = 1 << 6;
pub const DACB_MASK : u8 = 1 << 7;
pub const PWRCNTRL3 : u16 = 0x013;
pub const SPI_TXEN : u8 = 1 << 0;
pub const ENA_SPI_TXEN : u8 = 1 << 1;
pub const SPI_PA_CTRL : u8 = 1 << 2;
pub const ENA_PA_CTRL_FROM_SPI : u8 = 1 << 3;
pub const ENA_PA_CTRL_FROM_BLSM : u8 = 1 << 4;
pub const ENA_PA_CTRL_FROM_TXENSM : u8 = 1 << 5;
pub const ENA_PA_CTRL_FROM_PARROT_ERR : u8 = 1 << 6;
pub const GROUP_DLY : u16 = 0x014;
pub const COARSE_GROUP_DELAY : u8 = 1 << 0;
pub const GROUP_DELAY_RESERVED : u8 = 1 << 4;
pub const IRQEN_STATUSMODE0 : u16 = 0x01f;
pub const IRQEN_SMODE_LANEFIFOERR : u8 = 1 << 1;
pub const IRQEN_SMODE_SERPLLLOCK : u8 = 1 << 2;
pub const IRQEN_SMODE_SERPLLLOST : u8 = 1 << 3;
pub const IRQEN_SMODE_DACPLLLOCK : u8 = 1 << 4;
pub const IRQEN_SMODE_DACPLLLOST : u8 = 1 << 5;
pub const IRQEN_STATUSMODE1 : u16 = 0x020;
pub const IRQEN_SMODE_PRBS0 : u8 = 1 << 0;
pub const IRQEN_SMODE_PRBS1 : u8 = 1 << 1;
pub const IRQEN_SMODE_PRBS2 : u8 = 1 << 2;
pub const IRQEN_SMODE_PRBS3 : u8 = 1 << 3;
pub const IRQEN_STATUSMODE2 : u16 = 0x021;
pub const IRQEN_SMODE_SYNC_TRIP0 : u8 = 1 << 0;
pub const IRQEN_SMODE_SYNC_WLIM0 : u8 = 1 << 1;
pub const IRQEN_SMODE_SYNC_ROTATE0 : u8 = 1 << 2;
pub const IRQEN_SMODE_SYNC_LOCK0 : u8 = 1 << 3;
pub const IRQEN_SMODE_NCO_ALIGN0 : u8 = 1 << 4;
pub const IRQEN_SMODE_BLNKDONE0 : u8 = 1 << 5;
pub const IRQEN_SMODE_PDPERR0 : u8 = 1 << 7;
pub const IRQEN_STATUSMODE3 : u16 = 0x022;
pub const IRQEN_SMODE_SYNC_TRIP1 : u8 = 1 << 0;
pub const IRQEN_SMODE_SYNC_WLIM1 : u8 = 1 << 1;
pub const IRQEN_SMODE_SYNC_ROTATE1 : u8 = 1 << 2;
pub const IRQEN_SMODE_SYNC_LOCK1 : u8 = 1 << 3;
pub const IRQEN_SMODE_NCO_ALIGN1 : u8 = 1 << 4;
pub const IRQEN_SMODE_BLNKDONE1 : u8 = 1 << 5;
pub const IRQEN_SMODE_PDPERR1 : u8 = 1 << 7;
pub const IRQ_STATUS0 : u16 = 0x023;
pub const LANEFIFOERR : u8 = 1 << 1;
pub const SERPLLLOCK : u8 = 1 << 2;
pub const SERPLLLOST : u8 = 1 << 3;
pub const DACPLLLOCK : u8 = 1 << 4;
pub const DACPLLLOST : u8 = 1 << 5;
pub const IRQ_STATUS1 : u16 = 0x024;
pub const PRBS0 : u8 = 1 << 0;
pub const PRBS1 : u8 = 1 << 1;
pub const PRBS2 : u8 = 1 << 2;
pub const PRBS3 : u8 = 1 << 3;
pub const IRQ_STATUS2 : u16 = 0x025;
pub const SYNC_TRIP0 : u8 = 1 << 0;
pub const SYNC_WLIM0 : u8 = 1 << 1;
pub const SYNC_ROTATE0 : u8 = 1 << 2;
pub const SYNC_LOCK0 : u8 = 1 << 3;
pub const NCO_ALIGN0 : u8 = 1 << 4;
pub const BLNKDONE0 : u8 = 1 << 5;
pub const PDPERR0 : u8 = 1 << 7;
pub const IRQ_STATUS3 : u16 = 0x026;
pub const SYNC_TRIP1 : u8 = 1 << 0;
pub const SYNC_WLIM1 : u8 = 1 << 1;
pub const SYNC_ROTATE1 : u8 = 1 << 2;
pub const SYNC_LOCK1 : u8 = 1 << 3;
pub const NCO_ALIGN1 : u8 = 1 << 4;
pub const BLNKDONE1 : u8 = 1 << 5;
pub const PDPERR1 : u8 = 1 << 7;
pub const JESD_CHECKS : u16 = 0x030;
pub const ERR_INTSUPP : u8 = 1 << 0;
pub const ERR_SUBCLASS : u8 = 1 << 1;
pub const ERR_KUNSUPP : u8 = 1 << 2;
pub const ERR_JESDBAD : u8 = 1 << 3;
pub const ERR_WINLIMIT : u8 = 1 << 4;
pub const ERR_DLYOVER : u8 = 1 << 5;
pub const SYNC_ERRWINDOW : u16 = 0x034;
pub const SYNC_LASTERR_L : u16 = 0x038;
pub const SYNC_LASTERR_H : u16 = 0x039;
pub const LASTERROR_H : u8 = 1 << 0;
pub const LASTOVER : u8 = 1 << 6;
pub const LASTUNDER : u8 = 1 << 7;
pub const SYNC_CONTROL : u16 = 0x03a;
pub const SYNCMODE : u8 = 1 << 0;
pub const SYNCCLRLAST : u8 = 1 << 4;
pub const SYNCCLRSTKY : u8 = 1 << 5;
pub const SYNCARM : u8 = 1 << 6;
pub const SYNCENABLE : u8 = 1 << 7;
pub const SYNC_STATUS : u16 = 0x03b;
pub const SYNC_TRIP : u8 = 1 << 0;
pub const SYNC_WLIM : u8 = 1 << 1;
pub const SYNC_ROTATE : u8 = 1 << 2;
pub const SYNC_LOCK : u8 = 1 << 3;
pub const SYNC_BUSY : u8 = 1 << 7;
pub const SYNC_CURRERR_L : u16 = 0x03c;
pub const SYNC_CURRERR_H : u16 = 0x03d;
pub const CURRERROR_H : u8 = 1 << 0;
pub const CURROVER : u8 = 1 << 6;
pub const CURRUNDER : u8 = 1 << 7;
pub const DACGAIN0_I : u16 = 0x040;
pub const DACGAIN1_I : u16 = 0x041;
pub const DACGAIN0_Q : u16 = 0x042;
pub const DACGAIN1_Q : u16 = 0x043;
pub const GROUPDELAY_COMP_I : u16 = 0x044;
pub const GROUPDELAY_COMP_Q : u16 = 0x045;
pub const GROUPDELAY_COMP_BYP : u16 = 0x046;
pub const GROUPCOMP_BYPQ : u8 = 1 << 0;
pub const GROUPCOMP_BYPI : u8 = 1 << 1;
pub const MIX_MODE : u16 = 0x04a;
pub const NCOALIGN_MODE : u16 = 0x050;
pub const NCO_ALIGN_MODE : u8 = 1 << 0;
pub const NCO_ALIGN_FAIL : u8 = 1 << 3;
pub const NCO_ALIGN_PASS : u8 = 1 << 4;
pub const NCO_ALIGN_MTCH : u8 = 1 << 5;
pub const NCO_ALIGN_ARM : u8 = 1 << 7;
pub const NCOKEY_ILSB : u16 = 0x051;
pub const NCOKEY_IMSB : u16 = 0x052;
pub const NCOKEY_QLSB : u16 = 0x053;
pub const NCOKEY_QMSB : u16 = 0x054;
pub const PDP_THRES0 : u16 = 0x060;
pub const PDP_THRES1 : u16 = 0x061;
pub const PDP_AVG_TIME : u16 = 0x062;
pub const PDP_AVG_TIME_ : u8 = 1 << 0;
pub const PA_BUS_SWAP : u8 = 1 << 6;
pub const PDP_ENABLE : u8 = 1 << 7;
pub const PDP_POWER0 : u16 = 0x063;
pub const PDP_POWER1 : u16 = 0x064;
pub const CLKCFG0 : u16 = 0x080;
pub const REF_CLKDIV_EN : u8 = 1 << 0;
pub const RF_SYNC_EN : u8 = 1 << 1;
pub const DUTY_EN : u8 = 1 << 2;
pub const PD_CLK_REC : u8 = 1 << 3;
pub const PD_SERDES_PCLK : u8 = 1 << 4;
pub const PD_CLK_DIG : u8 = 1 << 5;
pub const PD_CLK23 : u8 = 1 << 6;
pub const PD_CLK01 : u8 = 1 << 7;
pub const SYSREF_ACTRL0 : u16 = 0x081;
pub const HYS_CNTRL1 : u8 = 1 << 0;
pub const SYSREF_RISE : u8 = 1 << 2;
pub const HYS_ON : u8 = 1 << 3;
pub const PD_SYSREF_BUFFER : u8 = 1 << 4;
pub const SYSREF_ACTRL1 : u16 = 0x082;
pub const DACPLLCNTRL : u16 = 0x083;
pub const ENABLE_DACPLL : u8 = 1 << 4;
pub const RECAL_DACPLL : u8 = 1 << 7;
pub const DACPLLSTATUS : u16 = 0x084;
pub const DACPLL_LOCK : u8 = 1 << 1;
pub const VCO_CAL_PROGRESS : u8 = 1 << 3;
pub const CP_CAL_VALID : u8 = 1 << 4;
pub const CP_OVERRANGE_L : u8 = 1 << 5;
pub const CP_OVERRANGE_H : u8 = 1 << 6;
pub const DACINTEGERWORD0 : u16 = 0x085;
pub const DACLOOPFILT1 : u16 = 0x087;
pub const LF_C1_WORD : u8 = 1 << 0;
pub const LF_C2_WORD : u8 = 1 << 4;
pub const DACLOOPFILT2 : u16 = 0x088;
pub const LF_C3_WORD : u8 = 1 << 0;
pub const LF_R1_WORD : u8 = 1 << 4;
pub const DACLOOPFILT3 : u16 = 0x089;
pub const LF_R3_WORD : u8 = 1 << 0;
pub const LF_BYPASS_C1 : u8 = 1 << 4;
pub const LF_BYPASS_C2 : u8 = 1 << 5;
pub const LF_BYPASS_R1 : u8 = 1 << 6;
pub const LF_BYPASS_R3 : u8 = 1 << 7;
pub const DACCPCNTRL : u16 = 0x08a;
pub const CP_CURRENT : u8 = 1 << 0;
pub const VT_FORCE : u8 = 1 << 6;
pub const DACLOGENCNTRL : u16 = 0x08b;
pub const LODIVMODE : u8 = 1 << 0;
pub const LO_POWER_MODE : u8 = 1 << 4;
pub const DACLDOCNTRL1 : u16 = 0x08c;
pub const REFDIVMODE : u8 = 1 << 0;
pub const LDO_BYPASS_FLT : u8 = 1 << 6;
pub const LDO_REF_SEL : u8 = 1 << 7;
pub const DACLDOCNTRL2 : u16 = 0x08d;
pub const LDO_VDROP : u8 = 1 << 0;
pub const LDO_SEL : u8 = 1 << 2;
pub const LDO_INRUSH : u8 = 1 << 5;
pub const LDO_BYPASS : u8 = 1 << 7;
pub const DATA_FORMAT : u16 = 0x110;
pub const BINARY_FORMAT : u8 = 1 << 7;
pub const DATAPATH_CTRL : u16 = 0x111;
pub const I_TO_Q : u8 = 1 << 0;
pub const SEL_SIDEBAND : u8 = 1 << 1;
pub const MODULATION_TYPE : u8 = 1 << 2;
pub const PHASE_ADJ_ENABLE : u8 = 1 << 4;
pub const DIG_GAIN_ENABLE : u8 = 1 << 5;
pub const INVSINC_ENABLE : u8 = 1 << 7;
pub const INTERP_MODE : u16 = 0x112;
pub const NCO_FTW_UPDATE : u16 = 0x113;
pub const FTW_UPDATE_REQ : u8 = 1 << 0;
pub const FTW_UPDATE_ACK : u8 = 1 << 1;
pub const FTW0 : u16 = 0x114;
pub const FTW1 : u16 = 0x115;
pub const FTW2 : u16 = 0x116;
pub const FTW3 : u16 = 0x117;
pub const FTW4 : u16 = 0x118;
pub const FTW5 : u16 = 0x119;
pub const NCO_PHASE_OFFSET0 : u16 = 0x11a;
pub const NCO_PHASE_OFFSET1 : u16 = 0x11b;
pub const PHASE_ADJ0 : u16 = 0x11c;
pub const PHASE_ADJ1 : u16 = 0x11d;
pub const TXEN_SM_0 : u16 = 0x11f;
pub const TXEN_SM_EN : u8 = 1 << 0;
pub const GP_PA_CTRL : u8 = 1 << 1;
pub const GP_PA_ON_INVERT : u8 = 1 << 2;
pub const RISE_COUNTERS : u8 = 1 << 4;
pub const FALL_COUNTERS : u8 = 1 << 6;
pub const TXEN_RISE_COUNT_0 : u16 = 0x121;
pub const TXEN_RISE_COUNT_1 : u16 = 0x122;
pub const TXEN_FALL_COUNT_0 : u16 = 0x123;
pub const TXEN_FALL_COUNT_1 : u16 = 0x124;
pub const DEVICE_CONFIG_REG_0 : u16 = 0x12d;
pub const DIE_TEMP_CTRL0 : u16 = 0x12f;
pub const AUXADC_ENABLE : u8 = 1 << 0;
pub const AUXADC_RESERVED : u8 = 1 << 1;
pub const DIE_TEMP0 : u16 = 0x132;
pub const DIE_TEMP1 : u16 = 0x133;
pub const DIE_TEMP_UPDATE : u16 = 0x134;
pub const DC_OFFSET_CTRL : u16 = 0x135;
pub const IPATH_DC_OFFSET_1PART0 : u16 = 0x136;
pub const IPATH_DC_OFFSET_1PART1 : u16 = 0x137;
pub const QPATH_DC_OFFSET_1PART0 : u16 = 0x138;
pub const QPATH_DC_OFFSET_1PART1 : u16 = 0x139;
pub const IPATH_DC_OFFSET_2PART : u16 = 0x13a;
pub const QPATH_DC_OFFSET_2PART : u16 = 0x13b;
pub const IDAC_DIG_GAIN0 : u16 = 0x13c;
pub const IDAC_DIG_GAIN1 : u16 = 0x13d;
pub const QDAC_DIG_GAIN0 : u16 = 0x13e;
pub const QDAC_DIG_GAIN1 : u16 = 0x13f;
pub const GAIN_RAMP_UP_STEP0 : u16 = 0x140;
pub const GAIN_RAMP_UP_STEP1 : u16 = 0x141;
pub const GAIN_RAMP_DOWN_STEP0 : u16 = 0x142;
pub const GAIN_RAMP_DOWN_STEP1 : u16 = 0x143;
pub const DEVICE_CONFIG_REG_1 : u16 = 0x146;
pub const BSM_STAT : u16 = 0x147;
pub const SOFTBLANKRB : u8 = 1 << 6;
pub const PRBS : u16 = 0x14b;
pub const PRBS_EN : u8 = 1 << 0;
pub const PRBS_RESET : u8 = 1 << 1;
pub const PRBS_MODE : u8 = 1 << 2;
pub const PRBS_GOOD_I : u8 = 1 << 6;
pub const PRBS_GOOD_Q : u8 = 1 << 7;
pub const PRBS_ERROR_I : u16 = 0x14c;
pub const PRBS_ERROR_Q : u16 = 0x14d;
pub const DACPLLT0 : u16 = 0x1b0;
pub const LOGEN_PD : u8 = 1 << 1;
pub const LDO_PD : u8 = 1 << 3;
pub const SYNTH_PD : u8 = 1 << 4;
pub const VCO_PD_ALC : u8 = 1 << 5;
pub const VCO_PD_PTAT : u8 = 1 << 6;
pub const VCO_PD_IN : u8 = 1 << 7;
pub const DACPLLT1 : u16 = 0x1b1;
pub const PFD_EDGE : u8 = 1 << 1;
pub const PFD_DELAY : u8 = 1 << 2;
pub const DACPLLT2 : u16 = 0x1b2;
pub const EXT_ALC_WORD : u8 = 1 << 0;
pub const EXT_ALC_WORD_EN : u8 = 1 << 7;
pub const DACPLLT3 : u16 = 0x1b3;
pub const EXT_BAND1 : u8 = 1 << 0;
pub const DACPLLT4 : u16 = 0x1b4;
pub const EXT_BAND2 : u8 = 1 << 0;
pub const EXT_BAND_EN : u8 = 1 << 1;
pub const VCO_CAL_OFFSET : u8 = 1 << 3;
pub const BYP_LOAD_DELAY : u8 = 1 << 7;
pub const DACPLLT5 : u16 = 0x1b5;
pub const DACPLLT6 : u16 = 0x1b6;
pub const DACPLLT7 : u16 = 0x1b7;
pub const DACPLLT8 : u16 = 0x1b8;
pub const DACPLLT9 : u16 = 0x1b9;
pub const DACPLLTA : u16 = 0x1ba;
pub const DACPLLTB : u16 = 0x1bb;
pub const VCO_BIAS_REF : u8 = 1 << 0;
pub const VCO_BIAS_TCF : u8 = 1 << 3;
pub const DACPLLTC : u16 = 0x1bc;
pub const DACPLLTD : u16 = 0x1bd;
pub const DACPLLTE : u16 = 0x1be;
pub const DACPLLTF : u16 = 0x1bf;
pub const DACPLLT10 : u16 = 0x1c0;
pub const DACPLLT11 : u16 = 0x1c1;
pub const DACPLLT15 : u16 = 0x1c2;
pub const DACPLLT16 : u16 = 0x1c3;
pub const DACPLLT17 : u16 = 0x1c4;
pub const DACPLLT18 : u16 = 0x1c5;
pub const MASTER_PD : u16 = 0x200;
pub const PHY_PD : u16 = 0x201;
pub const GENERIC_PD : u16 = 0x203;
pub const PD_SYNCOUT1B : u8 = 1 << 0;
pub const PD_SYNCOUT0B : u8 = 1 << 1;
pub const CDR_RESET : u16 = 0x206;
pub const CDR_OPERATING_MODE_REG_0 : u16 = 0x230;
pub const CDR_OVERSAMP : u8 = 1 << 1;
pub const CDR_RESERVED : u8 = 1 << 2;
pub const ENHALFRATE : u8 = 1 << 5;
pub const EQ_BIAS_REG : u16 = 0x268;
pub const EQ_BIAS_RESERVED : u8 = 1 << 0;
pub const EQ_POWER_MODE : u8 = 1 << 6;
pub const SERDESPLL_ENABLE_CNTRL : u16 = 0x280;
pub const ENABLE_SERDESPLL : u8 = 1 << 0;
pub const RECAL_SERDESPLL : u8 = 1 << 2;
pub const PLL_STATUS : u16 = 0x281;
pub const SERDES_PLL_LOCK_RB : u8 = 1 << 0;
pub const SERDES_CURRENTS_READY_RB : u8 = 1 << 1;
pub const SERDES_VCO_CAL_IN_PROGRESS_RB : u8 = 1 << 2;
pub const SERDES_PLL_CAL_VALID_RB : u8 = 1 << 3;
pub const SERDES_PLL_OVERRANGE_L_RB : u8 = 1 << 4;
pub const SERDES_PLL_OVERRANGE_H_RB : u8 = 1 << 5;
pub const LDO_FILTER_1 : u16 = 0x284;
pub const LDO_FILTER_2 : u16 = 0x285;
pub const LDO_FILTER_3 : u16 = 0x286;
pub const CP_CURRENT_SPI : u16 = 0x287;
pub const SPI_CP_CURRENT : u8 = 1 << 0;
pub const SPI_SERDES_LOGEN_POWER_MODE : u8 = 1 << 6;
pub const REF_CLK_DIVIDER_LDO : u16 = 0x289;
pub const SPI_CDR_OVERSAMP : u8 = 1 << 0;
pub const SPI_LDO_BYPASS_FILT : u8 = 1 << 2;
pub const SPI_LDO_REF_SEL : u8 = 1 << 3;
pub const VCO_LDO : u16 = 0x28a;
pub const PLL_RD_REG : u16 = 0x28b;
pub const SPI_SERDES_LOGEN_PD_CORE : u8 = 1 << 0;
pub const SPI_SERDES_LDO_PD : u8 = 1 << 2;
pub const SPI_SYN_PD : u8 = 1 << 3;
pub const SPI_VCO_PD_ALC : u8 = 1 << 4;
pub const SPI_VCO_PD_PTAT : u8 = 1 << 5;
pub const SPI_VCO_PD : u8 = 1 << 6;
pub const ALC_VARACTOR : u16 = 0x290;
pub const SPI_VCO_VARACTOR : u8 = 1 << 0;
pub const SPI_INIT_ALC_VALUE : u8 = 1 << 4;
pub const VCO_OUTPUT : u16 = 0x291;
pub const SPI_VCO_OUTPUT_LEVEL : u8 = 1 << 0;
pub const SPI_VCO_OUTPUT_RESERVED : u8 = 1 << 4;
pub const CP_CONFIG : u16 = 0x294;
pub const SPI_CP_TEST : u8 = 1 << 0;
pub const SPI_CP_CAL_EN : u8 = 1 << 2;
pub const SPI_CP_FORCE_CALBITS : u8 = 1 << 3;
pub const SPI_CP_OFFSET_OFF : u8 = 1 << 4;
pub const SPI_CP_ENABLE_MACHINE : u8 = 1 << 5;
pub const SPI_CP_DITHER_MODE : u8 = 1 << 6;
pub const SPI_CP_HALF_VCO_CAL_CLK : u8 = 1 << 7;
pub const VCO_BIAS_1 : u16 = 0x296;
pub const SPI_VCO_BIAS_REF : u8 = 1 << 0;
pub const SPI_VCO_BIAS_TCF : u8 = 1 << 3;
pub const VCO_BIAS_2 : u16 = 0x297;
pub const SPI_PRESCALE_BIAS : u8 = 1 << 0;
pub const SPI_LAST_ALC_EN : u8 = 1 << 2;
pub const SPI_PRESCALE_BYPASS_R : u8 = 1 << 3;
pub const SPI_VCO_COMP_BYPASS_BIASR : u8 = 1 << 4;
pub const SPI_VCO_BYPASS_DAC_R : u8 = 1 << 5;
pub const VCO_PD_OVERRIDES : u16 = 0x299;
pub const SPI_VCO_PD_OVERRIDE_VCO_BUF : u8 = 1 << 0;
pub const SPI_VCO_PD_OVERRIDE_CAL_TCF : u8 = 1 << 1;
pub const SPI_VCO_PD_OVERRIDE_VAR_REF_TCF : u8 = 1 << 2;
pub const SPI_VCO_PD_OVERRIDE_VAR_REF : u8 = 1 << 3;
pub const VCO_CAL : u16 = 0x29a;
pub const SPI_FB_CLOCK_ADV : u8 = 1 << 0;
pub const SPI_VCO_CAL_COUNT : u8 = 1 << 2;
pub const SPI_VCO_CAL_ALC_WAIT : u8 = 1 << 4;
pub const SPI_VCO_CAL_EN : u8 = 1 << 7;
pub const CP_LEVEL_DETECT : u16 = 0x29c;
pub const SPI_CP_LEVEL_THRESHOLD_HIGH : u8 = 1 << 0;
pub const SPI_CP_LEVEL_THRESHOLD_LOW : u8 = 1 << 3;
pub const SPI_CP_LEVEL_DET_PD : u8 = 1 << 6;
pub const VCO_VARACTOR_CTRL_0 : u16 = 0x29f;
pub const SPI_VCO_VARACTOR_OFFSET : u8 = 1 << 0;
pub const SPI_VCO_VARACTOR_REF_TCF : u8 = 1 << 4;
pub const VCO_VARACTOR_CTRL_1 : u16 = 0x2a0;
pub const SPI_VCO_VARACTOR_REF : u8 = 1 << 0;
pub const TERM_BLK1_CTRLREG0 : u16 = 0x2a7;
pub const TERM_BLK2_CTRLREG0 : u16 = 0x2ae;
pub const GENERAL_JRX_CTRL_0 : u16 = 0x300;
pub const LINK_EN : u8 = 1 << 0;
pub const LINK_PAGE : u8 = 1 << 2;
pub const LINK_MODE : u8 = 1 << 3;
pub const CHECKSUM_MODE : u8 = 1 << 6;
pub const GENERAL_JRX_CTRL_1 : u16 = 0x301;
pub const DYN_LINK_LATENCY_0 : u16 = 0x302;
pub const DYN_LINK_LATENCY_1 : u16 = 0x303;
pub const LMFC_DELAY_0 : u16 = 0x304;
pub const LMFC_DELAY_1 : u16 = 0x305;
pub const LMFC_VAR_0 : u16 = 0x306;
pub const LMFC_VAR_1 : u16 = 0x307;
pub const XBAR_LN_0_1 : u16 = 0x308;
pub const LOGICAL_LANE0_SRC : u8 = 1 << 0;
pub const LOGICAL_LANE1_SRC : u8 = 1 << 3;
pub const XBAR_LN_2_3 : u16 = 0x309;
pub const LOGICAL_LANE2_SRC : u8 = 1 << 0;
pub const LOGICAL_LANE3_SRC : u8 = 1 << 3;
pub const XBAR_LN_4_5 : u16 = 0x30a;
pub const LOGICAL_LANE4_SRC : u8 = 1 << 0;
pub const LOGICAL_LANE5_SRC : u8 = 1 << 3;
pub const XBAR_LN_6_7 : u16 = 0x30b;
pub const LOGICAL_LANE6_SRC : u8 = 1 << 0;
pub const LOGICAL_LANE7_SRC : u8 = 1 << 3;
pub const FIFO_STATUS_REG_0 : u16 = 0x30c;
pub const FIFO_STATUS_REG_1 : u16 = 0x30d;
pub const SYNCB_GEN_1 : u16 = 0x312;
pub const SYNCB_ERR_DUR : u8 = 1 << 4;
pub const SERDES_SPI_REG : u16 = 0x314;
pub const PHY_PRBS_TEST_EN : u16 = 0x315;
pub const PHY_PRBS_TEST_CTRL : u16 = 0x316;
pub const PHY_TEST_RESET : u8 = 1 << 0;
pub const PHY_TEST_START : u8 = 1 << 1;
pub const PHY_PRBS_PAT_SEL : u8 = 1 << 2;
pub const PHY_SRC_ERR_CNT : u8 = 1 << 4;
pub const PHY_PRBS_TEST_THRESHOLD_LOBITS : u16 = 0x317;
pub const PHY_PRBS_TEST_THRESHOLD_MIDBITS : u16 = 0x318;
pub const PHY_PRBS_TEST_THRESHOLD_HIBITS : u16 = 0x319;
pub const PHY_PRBS_TEST_ERRCNT_LOBITS : u16 = 0x31a;
pub const PHY_PRBS_TEST_ERRCNT_MIDBITS : u16 = 0x31b;
pub const PHY_PRBS_TEST_ERRCNT_HIBITS : u16 = 0x31c;
pub const PHY_PRBS_TEST_STATUS : u16 = 0x31d;
pub const SHORT_TPL_TEST_0 : u16 = 0x32c;
pub const SHORT_TPL_TEST_EN : u8 = 1 << 0;
pub const SHORT_TPL_TEST_RESET : u8 = 1 << 1;
pub const SHORT_TPL_DAC_SEL : u8 = 1 << 2;
pub const SHORT_TPL_SP_SEL : u8 = 1 << 4;
pub const SHORT_TPL_TEST_1 : u16 = 0x32d;
pub const SHORT_TPL_TEST_2 : u16 = 0x32e;
pub const SHORT_TPL_TEST_3 : u16 = 0x32f;
pub const DEVICE_CONFIG_REG_2 : u16 = 0x333;
pub const JESD_BIT_INVERSE_CTRL : u16 = 0x334;
pub const DID_REG : u16 = 0x400;
pub const BID_REG : u16 = 0x401;
pub const BID_RD : u8 = 1 << 0;
pub const ADJCNT_RD : u8 = 1 << 4;
pub const LID0_REG : u16 = 0x402;
pub const LID0_RD : u8 = 1 << 0;
pub const PHADJ_RD : u8 = 1 << 5;
pub const ADJDIR_RD : u8 = 1 << 6;
pub const SCR_L_REG : u16 = 0x403;
pub const L_1_RD : u8 = 1 << 0;
pub const SCR_RD : u8 = 1 << 7;
pub const F_REG : u16 = 0x404;
pub const K_REG : u16 = 0x405;
pub const M_REG : u16 = 0x406;
pub const CS_N_REG : u16 = 0x407;
pub const N_1_RD : u8 = 1 << 0;
pub const CS_RD : u8 = 1 << 6;
pub const NP_REG : u16 = 0x408;
pub const NP_1_RD : u8 = 1 << 0;
pub const SUBCLASSV_RD : u8 = 1 << 5;
pub const S_REG : u16 = 0x409;
pub const S_1_RD : u8 = 1 << 0;
pub const JESDV_RD : u8 = 1 << 5;
pub const HD_CF_REG : u16 = 0x40a;
pub const CF_RD : u8 = 1 << 0;
pub const HD_RD : u8 = 1 << 7;
pub const RES1_REG : u16 = 0x40b;
pub const RES2_REG : u16 = 0x40c;
pub const CHECKSUM0_REG : u16 = 0x40d;
pub const COMPSUM0_REG : u16 = 0x40e;
pub const LID1_REG : u16 = 0x412;
pub const CHECKSUM1_REG : u16 = 0x415;
pub const COMPSUM1_REG : u16 = 0x416;
pub const LID2_REG : u16 = 0x41a;
pub const CHECKSUM2_REG : u16 = 0x41d;
pub const COMPSUM2_REG : u16 = 0x41e;
pub const LID3_REG : u16 = 0x422;
pub const CHECKSUM3_REG : u16 = 0x425;
pub const COMPSUM3_REG : u16 = 0x426;
pub const LID4_REG : u16 = 0x42a;
pub const CHECKSUM4_REG : u16 = 0x42d;
pub const COMPSUM4_REG : u16 = 0x42e;
pub const LID5_REG : u16 = 0x432;
pub const CHECKSUM5_REG : u16 = 0x435;
pub const COMPSUM5_REG : u16 = 0x436;
pub const LID6_REG : u16 = 0x43a;
pub const CHECKSUM6_REG : u16 = 0x43d;
pub const COMPSUM6_REG : u16 = 0x43e;
pub const LID7_REG : u16 = 0x442;
pub const CHECKSUM7_REG : u16 = 0x445;
pub const COMPSUM7_REG : u16 = 0x446;
pub const ILS_DID : u16 = 0x450;
pub const ILS_BID : u16 = 0x451;
pub const BID : u8 = 1 << 0;
pub const ADJCNT : u8 = 1 << 4;
pub const ILS_LID0 : u16 = 0x452;
pub const LID0 : u8 = 1 << 0;
pub const PHADJ : u8 = 1 << 5;
pub const ADJDIR : u8 = 1 << 6;
pub const ILS_SCR_L : u16 = 0x453;
pub const L_1 : u8 = 1 << 0;
pub const SCR : u8 = 1 << 7;
pub const ILS_F : u16 = 0x454;
pub const ILS_K : u16 = 0x455;
pub const ILS_M : u16 = 0x456;
pub const ILS_CS_N : u16 = 0x457;
pub const N_1 : u8 = 1 << 0;
pub const CS : u8 = 1 << 6;
pub const ILS_NP : u16 = 0x458;
pub const NP_1 : u8 = 1 << 0;
pub const SUBCLASSV : u8 = 1 << 5;
pub const ILS_S : u16 = 0x459;
pub const S_1 : u8 = 1 << 0;
pub const JESDV : u8 = 1 << 5;
pub const ILS_HD_CF : u16 = 0x45a;
pub const CF : u8 = 1 << 0;
pub const HD : u8 = 1 << 7;
pub const ILS_RES1 : u16 = 0x45b;
pub const ILS_RES2 : u16 = 0x45c;
pub const ILS_CHECKSUM : u16 = 0x45d;
pub const ERRCNTRMON : u16 = 0x46b;
pub const CNTRSEL : u8 = 1 << 0;
pub const LANESEL : u8 = 1 << 4;
pub const LANEDESKEW : u16 = 0x46c;
pub const BADDISPARITY : u16 = 0x46d;
pub const LANE_ADDR_DIS : u8 = 1 << 0;
pub const RST_ERR_CNTR_DIS : u8 = 1 << 5;
pub const DISABLE_ERR_CNTR_DIS : u8 = 1 << 6;
pub const RST_IRQ_DIS : u8 = 1 << 7;
pub const NIT_W : u16 = 0x46e;
pub const LANE_ADDR_NIT : u8 = 1 << 0;
pub const RST_ERR_CNTR_NIT : u8 = 1 << 5;
pub const DISABLE_ERR_CNTR_NIT : u8 = 1 << 6;
pub const RST_IRQ_NIT : u8 = 1 << 7;
pub const UNEXPECTEDCONTROL_W : u16 = 0x46f;
pub const LANE_ADDR_UCC : u8 = 1 << 0;
pub const RST_ERR_CNTR_UCC : u8 = 1 << 5;
pub const DISABLE_ERR_CNTR_UCC : u8 = 1 << 6;
pub const RST_IRQ_UCC : u8 = 1 << 7;
pub const CODEGRPSYNCFLG : u16 = 0x470;
pub const FRAMESYNCFLG : u16 = 0x471;
pub const GOODCHKSUMFLG : u16 = 0x472;
pub const INITLANESYNCFLG : u16 = 0x473;
pub const CTRLREG1 : u16 = 0x476;
pub const CTRLREG2 : u16 = 0x477;
pub const THRESHOLD_MASK_EN : u8 = 1 << 3;
pub const ILAS_MODE : u8 = 1 << 7;
pub const KVAL : u16 = 0x478;
pub const IRQVECTOR_MASK : u16 = 0x47a;
pub const CODEGRPSYNC_MASK : u8 = 1 << 0;
pub const BADCHECKSUM_MASK : u8 = 1 << 2;
pub const INITIALLANESYNC_MASK : u8 = 1 << 3;
pub const UCC_MASK : u8 = 1 << 5;
pub const NIT_MASK : u8 = 1 << 6;
pub const BADDIS_MASK : u8 = 1 << 7;
pub const SYNCASSERTIONMASK : u16 = 0x47b;
pub const CMM_ENABLE : u8 = 1 << 3;
pub const CMM : u8 = 1 << 4;
pub const UCC_S : u8 = 1 << 5;
pub const NIT_S : u8 = 1 << 6;
pub const BADDIS_S : u8 = 1 << 7;
pub const ERRORTHRES : u16 = 0x47c;
pub const LANEENABLE : u16 = 0x47d;
pub const RAMP_ENA : u16 = 0x47e;
pub const DIG_TEST0 : u16 = 0x520;
pub const DC_TEST_MODE : u8 = 1 << 1;
pub const DC_TEST_VALUEI0 : u16 = 0x521;
pub const DC_TEST_VALUEI1 : u16 = 0x522;
pub const DC_TEST_VALUEQ0 : u16 = 0x523;
pub const DC_TEST_VALUEQ1 : u16 = 0x524;

View File

@ -1,219 +0,0 @@
use board_misoc::{csr, clock, config};
#[cfg(feature = "alloc")]
use alloc::format;
struct SerdesConfig {
pub delay: [u8; 4],
}
impl SerdesConfig {
pub fn as_bytes(&self) -> &[u8] {
unsafe {
core::slice::from_raw_parts(
(self as *const SerdesConfig) as *const u8,
core::mem::size_of::<SerdesConfig>(),
)
}
}
}
fn select_lane(lane_no: u8) {
unsafe {
csr::eem_transceiver::lane_sel_write(lane_no);
}
}
fn apply_delay(tap: u8) {
unsafe {
csr::eem_transceiver::dly_cnt_in_write(tap);
csr::eem_transceiver::dly_ld_write(1);
clock::spin_us(1);
assert!(tap as u8 == csr::eem_transceiver::dly_cnt_out_read());
}
}
fn apply_config(config: &SerdesConfig) {
for lane_no in 0..4 {
select_lane(lane_no as u8);
apply_delay(config.delay[lane_no]);
}
}
unsafe fn assign_delay() -> SerdesConfig {
// Select an appropriate delay for lane 0
select_lane(0);
let read_align = |dly: u8| -> f32 {
apply_delay(dly);
csr::eem_transceiver::counter_reset_write(1);
csr::eem_transceiver::counter_enable_write(1);
clock::spin_us(2000);
csr::eem_transceiver::counter_enable_write(0);
let (high, low) = (
csr::eem_transceiver::counter_high_count_read(),
csr::eem_transceiver::counter_low_count_read(),
);
if csr::eem_transceiver::counter_overflow_read() == 1 {
panic!("Unexpected phase detector counter overflow");
}
low as f32 / (low + high) as f32
};
let mut best_dly = None;
loop {
let mut prev = None;
for curr_dly in 0..32 {
let curr_low_rate = read_align(curr_dly);
if let Some(prev_low_rate) = prev {
// This is potentially a crossover position
if prev_low_rate <= curr_low_rate && curr_low_rate >= 0.5 {
let prev_dev = 0.5 - prev_low_rate;
let curr_dev = curr_low_rate - 0.5;
let selected_idx = if prev_dev < curr_dev {
curr_dly - 1
} else {
curr_dly
};
// The setup setup/hold calibration timing (even with
// tolerance) might be invalid in other lanes due to skew.
// 5 taps is very conservative, generally it is 1 or 2
if selected_idx < 5 {
prev = None;
continue;
} else {
best_dly = Some(selected_idx);
break;
}
}
}
// Only rising slope from <= 0.5 can result in a rising low rate
// crossover at 50%.
if curr_low_rate <= 0.5 {
prev = Some(curr_low_rate);
}
}
if best_dly.is_none() {
error!("setup/hold timing calibration failed, retry in 1s...");
clock::spin_us(1_000_000);
} else {
break;
}
}
let best_dly = best_dly.unwrap();
apply_delay(best_dly);
let mut delay_list = [best_dly; 4];
// Assign delay for other lanes
for lane_no in 1..=3 {
select_lane(lane_no as u8);
let mut min_deviation = 0.5;
let mut min_idx = 0;
for dly_delta in -3..=3 {
let index = (best_dly as isize + dly_delta) as u8;
let low_rate = read_align(index);
// abs() from f32 is not available in core library
let deviation = if low_rate < 0.5 {
0.5 - low_rate
} else {
low_rate - 0.5
};
if deviation < min_deviation {
min_deviation = deviation;
min_idx = index;
}
}
apply_delay(min_idx);
delay_list[lane_no] = min_idx;
}
debug!("setup/hold timing calibration: {:?}", delay_list);
SerdesConfig {
delay: delay_list,
}
}
unsafe fn align_comma() {
loop {
for slip in 1..=10 {
// The soft transceiver has 2 8b10b decoders, which receives lane
// 0/1 and lane 2/3 respectively. The decoder are time-multiplexed
// to decode exactly 1 lane each sysclk cycle.
//
// The decoder decodes lane 0/2 data on odd sysclk cycles, buffer
// on even cycles, and vice versa for lane 1/3. Data/Clock latency
// could change timing. The extend bit flips the decoding timing,
// so lane 0/2 data are decoded on even cycles, and lane 1/3 data
// are decoded on odd cycles.
//
// This is needed because transmitting/receiving a 8b10b character
// takes 2 sysclk cycles. Adjusting bitslip only via ISERDES
// limits the range to 1 cycle. The wordslip bit extends the range
// to 2 sysclk cycles.
csr::eem_transceiver::wordslip_write((slip > 5) as u8);
// Apply a double bitslip since the ISERDES is 2x oversampled.
// Bitslip is used for comma alignment purposes once setup/hold
// timing is met.
csr::eem_transceiver::bitslip_write(1);
csr::eem_transceiver::bitslip_write(1);
clock::spin_us(1);
csr::eem_transceiver::comma_align_reset_write(1);
clock::spin_us(100);
if csr::eem_transceiver::comma_read() == 1 {
debug!("comma alignment completed after {} bitslips", slip);
return;
}
}
error!("comma alignment failed, retrying in 1s...");
clock::spin_us(1_000_000);
}
}
pub fn init() {
for trx_no in 0..csr::CONFIG_EEM_DRTIO_COUNT {
unsafe {
csr::eem_transceiver::transceiver_sel_write(trx_no as u8);
}
let key = format!("eem_drtio_delay{}", trx_no);
config::read(&key, |r| {
match r {
Ok(record) => {
info!("loading calibrated timing values from flash");
unsafe {
apply_config(&*(record.as_ptr() as *const SerdesConfig));
}
},
Err(_) => {
info!("calibrating...");
let config = unsafe { assign_delay() };
config::write(&key, config.as_bytes()).unwrap();
}
}
});
unsafe {
align_comma();
csr::eem_transceiver::rx_ready_write(1);
}
}
}

View File

@ -0,0 +1,419 @@
mod hmc830 {
use board_misoc::{csr, clock};
fn spi_setup() {
unsafe {
while csr::converter_spi::idle_read() == 0 {}
csr::converter_spi::offline_write(0);
csr::converter_spi::end_write(1);
csr::converter_spi::cs_polarity_write(0b0001);
csr::converter_spi::clk_polarity_write(0);
csr::converter_spi::clk_phase_write(0);
csr::converter_spi::lsb_first_write(0);
csr::converter_spi::half_duplex_write(0);
csr::converter_spi::length_write(32 - 1);
csr::converter_spi::div_write(16 - 2);
csr::converter_spi::cs_write(1 << csr::CONFIG_CONVERTER_SPI_HMC830_CS);
}
}
pub fn select_spi_mode() {
spi_setup();
unsafe {
// rising egde on CS since cs_polarity still 0
// selects "HMC Mode"
// do a dummy cycle with cs still high to clear CS
csr::converter_spi::length_write(0);
csr::converter_spi::data_write(0);
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::length_write(32 - 1);
}
}
fn write(addr: u8, data: u32) {
let val = ((addr as u32) << 24) | data;
unsafe {
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::data_write(val << 1); // last clk cycle loads data
}
}
fn read(addr: u8) -> u32 {
// SDO (miso/read bits) is technically CPHA=1, while SDI is CPHA=0
// trust that the 8.2ns+0.2ns/pF provide enough hold time on top of
// the SPI round trip delay and stick with CPHA=0
write((1 << 6) | addr, 0);
unsafe {
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::data_read() & 0xffffff
}
}
pub fn detect() -> Result<(), &'static str> {
spi_setup();
let id = read(0x00);
if id != 0xa7975 {
error!("invalid HMC830 ID: 0x{:08x}", id);
return Err("invalid HMC830 identification");
}
Ok(())
}
pub fn init() {
// Configure HMC830 for integer-N operation
// See "PLLs with integrated VCO- RF Applications Product & Operating
// Guide"
spi_setup();
info!("loading HMC830 configuration...");
write(0x0, 0x20); // software reset
write(0x0, 0x00); // normal operation
write(0x6, 0x307ca); // integer-N mode (NB data sheet table 5.8 not self-consistent)
write(0x7, 0x4d); // digital lock detect, 1/2 cycle window (6.5ns window)
write(0x9, 0x2850); // charge pump: 1.6mA, no offset
write(0xa, 0x2045); // for wideband devices like the HMC830
write(0xb, 0x7c061); // for HMC830
// VCO subsystem registers
// NB software reset does not seem to reset these registers, so always
// program them all!
write(0x5, 0xf88); // 1: defaults
write(0x5, 0x6010); // 2: mute output until output divider set
write(0x5, 0x2818); // 3: wideband PLL defaults
write(0x5, 0x60a0); // 4: HMC830 magic value
write(0x5, 0x1628); // 5: HMC830 magic value
write(0x5, 0x7fb0); // 6: HMC830 magic value
write(0x5, 0x0); // ready for VCO auto-cal
info!(" ...done");
}
pub fn set_dividers(r_div: u32, n_div: u32, m_div: u32, out_div: u32) {
// VCO frequency: f_vco = (f_ref/r_div)*(n_int + n_frac/2**24)
// VCO frequency range [1.5GHz, 3GHz]
// Output frequency: f_out = f_vco/out_div
// Max PFD frequency: 125MHz for integer-N, 100MHz for fractional
// (mode B)
// Max reference frequency: 350MHz, however f_ref >= 200MHz requires
// setting 0x08[21]=1
//
// Warning: Output divider is not synchronized! Set to 1 for deterministic
// phase at the output.
//
// :param r_div: reference divider [1, 16383]
// :param n_div: VCO divider, integer part. Integer-N mode: [16, 2**19-1]
// fractional mode: [20, 2**19-4]
// :param m_div: VCO divider, fractional part [0, 2**24-1]
// :param out_div: output divider [1, 62] (0 mutes output)
info!("setting HMC830 dividers...");
write(0x5, 0x6010 + (out_div << 7) + (((out_div <= 2) as u32) << 15));
write(0x5, 0x0); // ready for VCO auto-cal
write(0x2, r_div);
write(0x4, m_div);
write(0x3, n_div);
info!(" ...done");
}
pub fn check_locked() -> Result<(), &'static str> {
info!("waiting for HMC830 lock...");
let t = clock::get_ms();
while read(0x12) & 0x02 == 0 {
if clock::get_ms() > t + 2000 {
error!("lock timeout. Register dump:");
for addr in 0x00..0x14 {
// These registers don't exist (in the data sheet at least)
if addr == 0x0d || addr == 0x0e { continue; }
error!(" [0x{:02x}] = 0x{:04x}", addr, read(addr));
}
return Err("lock timeout");
}
}
info!(" ...locked");
Ok(())
}
}
pub mod hmc7043 {
use board_misoc::{csr, clock};
// Warning: dividers are not synchronized with HMC830 clock input!
// Set DAC_CLK_DIV to 1 or 0 for deterministic phase.
// (0 bypasses the divider and reduces noise)
const DAC_CLK_DIV: u16 = 0;
const FPGA_CLK_DIV: u16 = 16; // Keep in sync with jdcg.rs
const SYSREF_DIV: u16 = 256; // Keep in sync with jdcg.rs
const HMC_SYSREF_DIV: u16 = SYSREF_DIV*8; // must be <= 4MHz
// enabled, divider, output config, is sysref
const OUTPUT_CONFIG: [(bool, u16, u8, bool); 14] = [
(true, DAC_CLK_DIV, 0x08, false), // 0: DAC1_CLK
(true, SYSREF_DIV, 0x01, true), // 1: DAC1_SYSREF
(true, DAC_CLK_DIV, 0x08, false), // 2: DAC0_CLK
(true, SYSREF_DIV, 0x01, true), // 3: DAC0_SYSREF
(true, SYSREF_DIV, 0x10, true), // 4: AMC_FPGA_SYSREF0
(false, FPGA_CLK_DIV, 0x10, true), // 5: AMC_FPGA_SYSREF1
(false, 0, 0x10, false), // 6: unused
(true, FPGA_CLK_DIV, 0x10, true), // 7: RTM_FPGA_SYSREF0
(true, FPGA_CLK_DIV, 0x08, false), // 8: GTP_CLK0_IN
(false, 0, 0x10, false), // 9: unused
(false, 0, 0x10, false), // 10: unused
(false, 0, 0x08, false), // 11: unused / uFL
(false, 0, 0x10, false), // 12: unused
(false, FPGA_CLK_DIV, 0x10, true), // 13: RTM_FPGA_SYSREF1
];
fn spi_setup() {
unsafe {
while csr::converter_spi::idle_read() == 0 {}
csr::converter_spi::offline_write(0);
csr::converter_spi::end_write(1);
csr::converter_spi::cs_polarity_write(0b0001);
csr::converter_spi::clk_polarity_write(0);
csr::converter_spi::clk_phase_write(0);
csr::converter_spi::lsb_first_write(0);
csr::converter_spi::half_duplex_write(0); // change mid-transaction for reads
csr::converter_spi::length_write(24 - 1);
csr::converter_spi::div_write(16 - 2);
csr::converter_spi::cs_write(1 << csr::CONFIG_CONVERTER_SPI_HMC7043_CS);
}
}
fn spi_wait_idle() {
unsafe {
while csr::converter_spi::idle_read() == 0 {}
}
}
fn write(addr: u16, data: u8) {
let cmd = (0 << 15) | addr;
let val = ((cmd as u32) << 8) | data as u32;
unsafe {
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::data_write(val << 8);
}
}
fn read(addr: u16) -> u8 {
let cmd = (1 << 15) | addr;
let val = cmd as u32;
unsafe {
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::end_write(0);
csr::converter_spi::length_write(16 - 1);
csr::converter_spi::data_write(val << 16);
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::end_write(1);
csr::converter_spi::half_duplex_write(1);
csr::converter_spi::length_write(8 - 1);
csr::converter_spi::data_write(0);
while csr::converter_spi::writable_read() == 0 {}
csr::converter_spi::half_duplex_write(0);
csr::converter_spi::length_write(24 - 1);
csr::converter_spi::data_read() as u8
}
}
pub const CHIP_ID: u32 = 0xf17904;
pub fn get_id() -> u32 {
spi_setup();
(read(0x78) as u32) << 16 | (read(0x79) as u32) << 8 | read(0x7a) as u32
}
pub fn detect() -> Result<(), &'static str> {
let id = get_id();
if id != CHIP_ID {
error!("invalid HMC7043 ID: 0x{:08x}", id);
return Err("invalid HMC7043 identification");
}
Ok(())
}
pub fn enable() {
info!("enabling HMC7043");
unsafe {
csr::hmc7043_reset::out_write(0);
}
clock::spin_us(10_000);
spi_setup();
write(0x0, 0x1); // Software reset
write(0x0, 0x0); // Normal operation
write(0x1, 0x48); // mute all outputs
}
const GPO_MUX_CLK_OUT_PHASE: u8 = 3;
const GPO_MUX_FORCE1: u8 = 10;
const GPO_MUX_FORCE0: u8 = 11;
/* Read an HMC7043 internal status bit through the GPO interface.
* This method is required to work around bugs in the register interface.
*/
fn gpo_indirect_read(mux_setting: u8) -> bool {
write(0x50, (mux_setting << 2) | 0x3);
spi_wait_idle();
unsafe {
csr::hmc7043_gpo::in_read() == 1
}
}
pub fn init() {
spi_setup();
info!("loading configuration...");
write(0x3, 0x14); // Disable the RFSYNCIN reseeder
write(0xA, 0x06); // Disable the RFSYNCIN input buffer
write(0xB, 0x07); // Enable the CLKIN input as LVPECL
write(0x9F, 0x4d); // Unexplained high-performance mode
write(0xA0, 0xdf); // Unexplained high-performance mode
// Enable required output groups
let mut output_group_en = 0;
for channel in 0..OUTPUT_CONFIG.len() {
let enabled = OUTPUT_CONFIG[channel].0;
if enabled {
let group = channel/2;
output_group_en |= 1 << group;
}
}
write(0x4, output_group_en);
// Set SYSREF timer divider.
// We don't need this "feature", but the HMC7043 won't work without.
write(0x5c, (HMC_SYSREF_DIV & 0xff) as u8);
write(0x5d, ((HMC_SYSREF_DIV & 0xf00) >> 8) as u8);
for channel in 0..OUTPUT_CONFIG.len() {
let channel_base = 0xc8 + 0x0a*(channel as u16);
let (enabled, divider, outcfg, is_sysref) = OUTPUT_CONFIG[channel];
if enabled {
if !is_sysref {
// DCLK channel: enable high-performance mode
write(channel_base, 0xd1);
} else {
// SYSREF channel: disable hi-perf mode, enable slip
write(channel_base, 0x71);
}
} else {
write(channel_base, 0x10);
}
write(channel_base + 0x1, (divider & 0xff) as u8);
write(channel_base + 0x2, ((divider & 0xf00) >> 8) as u8);
// bypass analog phase shift on DCLK channels to reduce noise
if !is_sysref {
if divider != 0 {
write(channel_base + 0x7, 0x00); // enable divider
} else {
write(channel_base + 0x7, 0x03); // bypass divider for lowest noise
}
} else {
write(channel_base + 0x7, 0x01);
}
write(channel_base + 0x8, outcfg)
}
write(0x1, 0x4a); // Reset dividers and FSMs
write(0x1, 0x48);
write(0x1, 0xc8); // Synchronize dividers
write(0x1, 0x40); // Unmute, high-performance/low-noise mode
clock::spin_us(10_000);
info!(" ...done");
}
pub fn test_gpo() -> Result<(), &'static str> {
info!("testing GPO...");
for trial in 0..10 {
if !gpo_indirect_read(GPO_MUX_FORCE1) {
info!(" ...failed. GPO I/O did not go high (#{})", trial + 1);
return Err("GPO is not functioning");
}
if gpo_indirect_read(GPO_MUX_FORCE0) {
info!(" ...failed. GPO I/O did not return low (#{})", trial + 1);
return Err("GPO is not functioning");
}
}
info!(" ...passed");
Ok(())
}
pub fn check_phased() -> Result<(), &'static str> {
if !gpo_indirect_read(GPO_MUX_CLK_OUT_PHASE) {
return Err("GPO reported phases did not align");
}
// Should be the same as the GPO read
let sysref_fsm_status = read(0x91);
if sysref_fsm_status != 0x2 {
error!("Bad SYSREF FSM status: {:02x}", sysref_fsm_status);
return Err("Bad SYSREF FSM status");
}
Ok(())
}
pub fn unmute() {
/*
* Never missing an opportunity to be awful, the HMC7043 produces broadband noise
* prior to intialization, which can upset the AMC FPGA.
* External circuitry mutes it.
*/
unsafe {
csr::hmc7043_out_en::out_write(1);
}
}
pub fn sysref_delay_dac(dacno: u8, phase_offset: u8) {
spi_setup();
if dacno == 0 {
write(0x00e9, phase_offset);
} else if dacno == 1 {
write(0x00d5, phase_offset);
} else {
unimplemented!();
}
clock::spin_us(100);
}
pub fn sysref_slip() {
spi_setup();
write(0x0002, 0x02);
write(0x0002, 0x00);
clock::spin_us(100);
}
}
pub fn init() -> Result<(), &'static str> {
#[cfg(all(hmc830_ref = "125", rtio_frequency = "125.0"))]
const DIV: (u32, u32, u32, u32) = (2, 32, 0, 1); // 125MHz -> 2.0GHz
#[cfg(all(hmc830_ref = "150", rtio_frequency = "150.0"))]
const DIV: (u32, u32, u32, u32) = (2, 32, 0, 1); // 150MHz -> 2.4GHz
/* do not use other SPI devices before HMC830 SPI mode selection */
hmc830::select_spi_mode();
hmc830::detect()?;
hmc830::init();
hmc830::set_dividers(DIV.0, DIV.1, DIV.2, DIV.3);
hmc830::check_locked()?;
if hmc7043::get_id() == hmc7043::CHIP_ID {
error!("HMC7043 detected while in reset (board rework missing?)");
}
hmc7043::enable();
hmc7043::detect()?;
hmc7043::init();
hmc7043::test_gpo()?;
hmc7043::check_phased()?;
hmc7043::unmute();
Ok(())
}

View File

@ -12,8 +12,6 @@ extern crate log;
extern crate io;
extern crate board_misoc;
extern crate proto_artiq;
#[cfg(feature = "alloc")]
extern crate alloc;
pub mod spi;
@ -25,15 +23,16 @@ pub mod rpc_queue;
#[cfg(has_si5324)]
pub mod si5324;
#[cfg(has_hmc830_7043)]
pub mod hmc830_7043;
#[cfg(has_ad9154)]
mod ad9154_reg;
#[cfg(has_ad9154)]
pub mod ad9154;
#[cfg(has_grabber)]
pub mod grabber;
#[cfg(has_drtio)]
pub mod drtioaux;
pub mod drtio_routing;
#[cfg(all(has_drtio_eem, feature = "alloc"))]
pub mod drtio_eem;
#[cfg(soc_platform = "efc")]
pub mod ad9117;

View File

@ -182,6 +182,12 @@ fn init() -> Result<()> {
i2c::switch_select(BUSNO, 0x70, 0)?;
i2c::switch_select(BUSNO, 0x71, 1 << 3)?;
}
#[cfg(soc_platform = "sayma_amc")]
i2c::switch_select(BUSNO, 0x70, 1 << 4)?;
#[cfg(soc_platform = "sayma_rtm")]
i2c::switch_select(BUSNO, 0x77, 1 << 5)?;
#[cfg(soc_platform = "metlino")]
i2c::switch_select(BUSNO, 0x70, 1 << 4)?;
#[cfg(soc_platform = "kc705")]
i2c::switch_select(BUSNO, 0x74, 1 << 7)?;

View File

@ -2,11 +2,9 @@
mod imp {
use board_misoc::csr;
const INVALID_BUS: &'static str = "Invalid SPI bus";
pub fn set_config(busno: u8, flags: u8, length: u8, div: u8, cs: u8) -> Result<(), &'static str> {
pub fn set_config(busno: u8, flags: u8, length: u8, div: u8, cs: u8) -> Result<(), ()> {
if busno != 0 {
return Err(INVALID_BUS)
return Err(())
}
unsafe {
while csr::converter_spi::writable_read() == 0 {}
@ -33,9 +31,9 @@ mod imp {
Ok(())
}
pub fn write(busno: u8, data: u32) -> Result<(), &'static str> {
pub fn write(busno: u8, data: u32) -> Result<(), ()> {
if busno != 0 {
return Err(INVALID_BUS)
return Err(())
}
unsafe {
while csr::converter_spi::writable_read() == 0 {}
@ -44,9 +42,9 @@ mod imp {
Ok(())
}
pub fn read(busno: u8) -> Result<u32, &'static str> {
pub fn read(busno: u8) -> Result<u32, ()> {
if busno != 0 {
return Err(INVALID_BUS)
return Err(())
}
Ok(unsafe {
while csr::converter_spi::writable_read() == 0 {}

View File

@ -15,7 +15,7 @@ build_misoc = { path = "../libbuild_misoc" }
[dependencies]
byteorder = { version = "1.0", default-features = false }
log = { version = "0.4", default-features = false, optional = true }
smoltcp = { version = "0.8.2", default-features = false, optional = true }
smoltcp = { version = "0.6.0", default-features = false, optional = true }
riscv = { version = "0.6.0", features = ["inline-asm"] }
[features]

View File

@ -81,31 +81,6 @@ impl IoExpander {
Ok(io_expander)
}
#[cfg(soc_platform = "efc")]
pub fn new() -> Result<Self, &'static str> {
const VIRTUAL_LED_MAPPING: [(u8, u8, u8); 2] = [(0, 0, 5), (1, 0, 6)];
let io_expander = IoExpander {
busno: 0,
port: 1,
address: 0x40,
virtual_led_mapping: &VIRTUAL_LED_MAPPING,
iodir: [0xff; 2],
out_current: [0; 2],
out_target: [0; 2],
registers: Registers {
iodira: 0x00,
iodirb: 0x01,
gpioa: 0x12,
gpiob: 0x13,
},
};
if !io_expander.check_ack()? {
return Err("MCP23017 io expander not found.");
};
Ok(io_expander)
}
#[cfg(soc_platform = "kasli")]
fn select(&self) -> Result<(), &'static str> {
let mask: u16 = 1 << self.port;
@ -114,13 +89,6 @@ impl IoExpander {
Ok(())
}
#[cfg(soc_platform = "efc")]
fn select(&self) -> Result<(), &'static str> {
let mask: u16 = 1 << self.port;
i2c::switch_select(self.busno, 0x70, mask as u8)?;
Ok(())
}
fn write(&self, addr: u8, value: u8) -> Result<(), &'static str> {
i2c::start(self.busno)?;
i2c::write(self.busno, self.address)?;

View File

@ -40,7 +40,7 @@ pub mod ethmac;
pub mod i2c;
#[cfg(soc_platform = "kasli")]
pub mod i2c_eeprom;
#[cfg(any(all(soc_platform = "kasli", hw_rev = "v2.0"), soc_platform = "efc"))]
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
pub mod io_expander;
#[cfg(all(has_ethmac, feature = "smoltcp"))]
pub mod net_settings;

Some files were not shown because too many files have changed in this diff Show More