forked from M-Labs/artiq
1
0
Fork 0

Compare commits

...

106 Commits

Author SHA1 Message Date
mwojcik 9a3f6f85a4 ad53xx: fix `load()` references in documentation 2023-10-16 07:56:32 +02:00
David Nadlinger 81d2b37b57 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:28:03 +08:00
Jonathan Coates f416b637c6 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:57 +08:00
Sebastien Bourdeauducq 76ee719a0d firmware: fix libproto_artiq compilation failure 2023-07-18 12:10:26 +08:00
sven-oxionics 2d017a05a1 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:44 +08:00
Florian Agbuya c73601b4b3 gui/experiments: cast Qt timestamp to int preventing float type error 2023-07-14 16:34:24 +08:00
mwojcik 9e6ffcfa30 fix missing DIFF_TERM for Sampler and Mirny inputs 2023-06-02 17:21:42 +08:00
Sebastien Bourdeauducq 837dffb56c fix IBUFDS_GTE2 parameters 2023-05-30 11:52:07 +08:00
Charles Baynham 76e93dc591 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 07:00:09 +08:00
Jonathan Coates cfc396cbc8 dma: fix off-by-one error in RawSlicer (#2090)
Signed-off-by: Jonathan Coates <jonathan.coates@oxionics.com>
2023-05-23 11:16:35 +08:00
Hartmann Michael (IFAG PSS SIS SCE QSE) 9c12d8b1de doc: conda installation notes 2023-05-12 17:45:38 +08:00
Ikko Eltociear Ashimine 8bd1f5c8b9 fix typo in comm_analyzer.py
error_occured -> error_occurred
occured -> occurred
2023-04-02 09:27:21 +08:00
Ikko Eltociear Ashimine 671ed8ac77 fix typo in developing_a_ndsp.rst
occurence -> occurrence
2023-03-11 18:33:06 +08:00
火焚 富良 b50d30ba5b compiler: fix const str/bytes handling (#1990) 2022-11-11 13:16:55 +08:00
cc78078 1b3fa3429d kasli: relocate the SatelliteBase Error LED code (#1955) 2022-08-12 13:42:59 +08:00
cc78078 004e7e7461 kasli: add Error LED to MasterBase and SatelliteBase 2022-08-11 15:08:52 +08:00
Sebastien Bourdeauducq ad37be40c7 typo 2022-07-21 11:58:50 +08:00
Sebastien Bourdeauducq a3ca686305 doc: update channels to legacy 2022-07-19 17:36:15 +08:00
Deepskyhunter 20dc923c9e Bugfix: Add missing item inside state to solve KeyError
KeyError raised when trying to load default_state()
due to missing Key "seed" in "RangeScan" and "CenterScan" in
state. Add {"seed": None} to resolve the bug.
2022-06-14 11:43:22 +08:00
Sebastien Bourdeauducq 83af3b756d Revert "compiler: Fix #1871 (array() breaks math functions)"
This reverts commit 8786b137a3.
2022-04-23 19:06:30 +08:00
Sebastien Bourdeauducq 3bc5a0dfb7 Revert "test: Fixup 6b5c390d4 typo"
This reverts commit 73371931fc.
2022-04-23 19:06:28 +08:00
David Nadlinger 73371931fc test: Fixup 6b5c390d4 typo 2022-04-22 09:00:46 +08:00
David Nadlinger 8786b137a3 compiler: Fix #1871 (array() breaks math functions)
GitHub: Fixes #1871.
2022-04-22 09:00:39 +08:00
Leon Riesebos ca67ae8365 browser: support datasets that use h5 group notation
Signed-off-by: Leon Riesebos <leon.riesebos@duke.edu>
2022-04-07 18:17:03 +08:00
Leon Riesebos 0226259e2b browser: cleanup datasets panel for empty h5 files
this fix makes sure the datasets panel is cleared if an h5 file is empty or the datasets and archive groups are empty
2022-04-07 18:17:03 +08:00
pca006132 c6a7b8a8dd compiler: fixed embedding annotation evaluation 2022-03-17 22:13:04 +08:00
pca006132 66b6555c72 compiler: allows string annotation
According to PEP484, type hint can be a string literal for forward
references. With PEP563, type hint would be preserved in annotations in
string form.
2022-03-17 22:12:58 +08:00
Sebastien Bourdeauducq 58f69cc96e mgmt: fix config write error message 2022-03-16 08:28:55 +08:00
mwojcik 2e65574c5f artiq_flash: fix satman fw for satellite1 etc boards 2022-03-10 16:22:13 +08:00
ciciwu 66ea41a81c phaser: fix docstring formatting (#1866) 2022-03-08 19:21:39 +08:00
Sebastien Bourdeauducq 56b8c3c096 dashboard: fix typo (#1858) 2022-02-26 08:57:28 +08:00
Steve Fan f941e17107 dashboard: prioritize min as part of default value resolution (#1839) 2022-01-27 17:49:21 +08:00
Steve Fan d7838e16dd comm_kernel: fix RPC exception handling (#1801) 2022-01-12 15:24:55 +08:00
Steve Fan 57e2ec629b comm_kernel: check if elements are within bounds for RPC list (#1824) 2022-01-11 17:17:57 +08:00
Peter Drmota 4f87531565 gateware.test.suservo: Fix tests for python >=3.7
Closes #1748
2022-01-11 17:17:57 +08:00
Steve Fan 64347290fb comm_kernel: fix off-by-one error for numeric value range check 2022-01-11 17:17:57 +08:00
Harry Ho f49f1fcbfc sayma_amc: add option to generate a 9MHz sq wave on the MCXs 2021-12-21 18:43:36 +08:00
Harry Ho 86fcd97416 sayma_amc: add option to force outputs on the MCXs 2021-12-21 18:43:36 +08:00
Steve Fan 759f00416b llvm_ir: move stacksave before lltag alloca in build_rpc
Signed-off-by: Steve Fan <sf@m-labs.hk>
2021-12-19 12:09:19 +08:00
Harry Ho 5818bc0878 mmcspi: fix timeout on detecting CS_n 2021-12-18 11:31:58 +08:00
Harry Ho 44171258f5 mmcspi: add timeout on detecting edges & data 2021-12-18 00:33:00 +08:00
Harry Ho bc249c32df sayma: fallback default MAC address if MMCSPI fails 2021-12-17 19:20:34 +08:00
Harry Ho 949917cc9c jdcg: fix missing JESD reset & JDAC re-init 2021-12-17 19:12:42 +08:00
Harry Ho dc411d55be jdcg: allow <=2 retries upon SYSREF test failure 2021-12-17 19:12:42 +08:00
Harry Ho 40e7b6058e sayma: support reading EUI48 from MMCSPI 2021-12-16 21:23:29 +08:00
Harry Ho 105af644bd libboard_misoc: add MMCSPI bitbanging driver
* Requires changes to Sayma AMC openMMC firmware as in https://github.com/HarryMakes/openMMC/commits/sayma-devel-fix/eui
* Primary supports reading EUI48 broadcasted from the MMC
2021-12-16 21:23:29 +08:00
Harry Ho 04ee775a9f sayma_amc: adopt MMCSPI bitbanging GPIO core
* Requires changes to migen as in https://github.com/HarryMakes/migen/commits/sayma-mmcspi
2021-12-16 21:23:29 +08:00
Sébastien Bourdeauducq 9547a15162
Merge pull request #1796 from HarryMakes/release-6
Metlino/Sayma: fix DDS sync & upgrade SAWG to 1 GS/s
2021-12-13 10:58:49 +08:00
Harry Ho d3869c966e examples: fix RTIO clocks to 125 MHz 2021-12-13 10:42:23 +08:00
Harry Ho 77c4d2f013 siphaser: remove support for 150 MHz RTIO clock
* Sayma AMC no longer uses 150 MHz RTIO clock after 1 GS/s upgrade.
2021-12-13 10:37:50 +08:00
Harry Ho 23f5796d67 sayma: fix FTW for SyncDDS 2021-12-10 12:13:09 +08:00
Harry Ho 34e89a3777 gth_ultrascale: fix TX/RX_CLK25_DIV for 125 MHz GTREFCLK0 2021-12-10 12:13:06 +08:00
Harry Ho a14666bc15 ad9154: re-adjust LMFCDel & LMFCVar for 1 GS/s (K=32)
* @HarryMakes performed 25 consecutive power-cycles of Sayma, in 2-min intervals:
  * Results: MinDelay = 8, FALL_COUNT_Delay = 10
2021-12-10 12:13:03 +08:00
Harry Ho 2f49a1a412 sayma: 600 MS/s -> 1 GS/s
* Merged from 'sayma1g' (d74cd24d)
2021-12-10 12:12:43 +08:00
Harry Ho 412936f8db ad9154: adjust LMFCDel & LMFCVar based on DYN_LINK_LATENCY readbacks
* @HarryMakes performed 25 consecutive power-cycles of Sayma, in 2-min intervals:
  * Results: MinDelay = 6, FALL_COUNT_Delay = 8 (w/ rollover)
2021-12-10 12:09:58 +08:00
Harry Ho 51e28de2f6 ad9154: check alignment phase error after one-shot sync 2021-12-10 12:09:58 +08:00
Harry Ho f5b9eab84b ad9154: fix sync 2021-12-10 12:09:58 +08:00
Harry Ho 9dfb0bfe1b gth_ultrascale: fix missing T/RXPROGDIVRESET 2021-12-10 12:09:58 +08:00
Sebastien Bourdeauducq 946254d22e artiq_sinara_tester: fix handling of IO_UPDATE with SU-Servo 2021-11-20 16:53:09 +08:00
Sebastien Bourdeauducq d9b01ed81a compiler: stop using sys.version_info for parser 2021-08-12 12:53:21 +08:00
Sebastien Bourdeauducq 9801aeb6a5 setup.py: remove outdated dependency_links 2021-08-12 12:53:16 +08:00
Sebastien Bourdeauducq 08b09f6dc3 artiq_run: fix multiarch 2021-08-12 12:48:23 +08:00
Sebastien Bourdeauducq dda4121c1d compiler: turn __repr__ into __str__ when sphinx is used. Closes #741 2021-08-05 11:52:41 +08:00
Sebastien Bourdeauducq 19daf91280 doc: nixpkgs 21.05 2021-07-27 09:32:35 +08:00
Sebastien Bourdeauducq 281b2182da artiq_flash: cleanup openocd handling, do not follow symlinks
Not following symlinks allows files to be added to OpenOCD via nixpkgs buildEnv.
2021-07-27 09:31:51 +08:00
Star Chen 414080554c moninj: fix read of incomplete data (#1729) 2021-07-22 17:57:45 +08:00
StarChen 7b523084b7 documentation: correct artiq_coremgmt examples 2021-07-19 12:10:32 +08:00
Sebastien Bourdeauducq c4902be6f8 doc: document shell-dev shortcut 2021-07-14 08:32:48 +08:00
occheung c6cd9ac2ea sinara_tester: add delay before adf5356 init 2021-07-07 12:35:53 +08:00
Sebastien Bourdeauducq 9741e4aa43 compiler: stop using deprecated numpy.float 2021-06-25 18:35:30 +08:00
Leon Riesebos ae137d1c9e artiq_flash: wrap paramiko commands in bash login shell
the login shell will load the nix environment on non-nixos systems

Signed-off-by: Leon Riesebos <leon.riesebos@duke.edu>
2021-06-25 11:28:26 +08:00
Star Chen 2f808880d5 Kasli: Added front panel user LED (#1623) (#1694) 2021-06-07 16:32:03 +08:00
pca006132 9033c59b75 aqctl_corelog: fix endianness issue (closes #1682) (#1689)
Fixed according to
https://forum.m-labs.hk/d/190-fetchingreading-the-core-log-in-a-central-location/10

Tested with both KC705 and ZC706.
2021-06-03 14:06:52 +08:00
Sebastien Bourdeauducq a80c35a606 artiq_ddb_template: kasli-soc support 2021-05-30 20:34:16 +08:00
pca006132 93e1bd9ba0 coredevice.comm_kernel: improved byte list performance. 2021-05-28 11:28:57 +08:00
David Nadlinger 65f0951f1a manual/compiler: Mention TArray annotation 2021-05-25 10:11:16 +08:00
Sebastien Bourdeauducq 040aa6fd9d artiq_flash: improve openocd not found error message 2021-05-13 14:45:43 +08:00
Sebastien Bourdeauducq a16c81a069 phaser: typo 2021-05-07 10:00:24 +08:00
Peter Drmota ec4270fb4b coredevice.comm_kernel: Fix unpacking of lists of numpy.int64
test.coredevice.test_embedding: Add tests for list of numpy.int64
2021-04-24 16:43:25 +08:00
Leon Riesebos 2d4fefe42e added DefaultMissing to __all__
Signed-off-by: Leon Riesebos <leon.riesebos@duke.edu>
2021-04-21 11:44:19 +08:00
Leon Riesebos 1619a32a1e ad99xx added additional kernel invariants
Signed-off-by: Leon Riesebos <leon.riesebos@duke.edu>
2021-04-21 11:19:20 +08:00
Leon Riesebos d000e06fbc ad99xx make kernel invariants instance variable
prevents mutations on class variable that applies to all instances at once
closes #1654

Signed-off-by: Leon Riesebos <leon.riesebos@duke.edu>
2021-04-21 11:19:20 +08:00
Marius Weber 95e292c8a2 fastino: ensure `xxx_to_mu()` methods return int32 on the host
Currently running `voltage_to_mu()` or `voltage_group_to_mu()` on the host will
convert all machine unit values to int64. This leads to issues when machine units
are returned from RPCs.

Signed-off-by: Marius Weber <marius.weber@physics.ox.ac.uk>
2021-04-19 16:50:27 +08:00
Harry Ho 0e3f23a86a jsonschema: mirny: fix clk_sel default value 2021-03-30 16:07:26 +08:00
Harry Ho dbeea7605b jsonschema: style 2021-03-30 14:35:01 +08:00
Harry Ho c6bfcdbf10 jsonschema: mirny: accept string enums for validating clk_sel 2021-03-30 14:35:01 +08:00
Harry Ho cd2e471b07 ddb_template: mirny_cpld: accept clk_sel as a string 2021-03-30 14:35:01 +08:00
Sebastien Bourdeauducq a23645291c manual: fix OpenOCD conda instructions 2021-03-27 12:15:34 +08:00
David Nadlinger 3f6840b736 dashboard: Disable Group CCB policy menu before first entry is selected
It was possible to crash the dashboard by opening the context menu
before an applet entry had been selected for the first time (e.g.
immediately after startup) and selecting one of the Group CCB
actions, as the enable update slot would not have been run.
2021-03-21 18:44:30 +08:00
David Nadlinger 9a9290a72d test/lit: Fix invalid type inference test
This broke after b8cd163978, but
is invalid code to start with; this would have previously
crashed the code generator had the code actually been compiled.

(Allowing implicit conversion to bool would be a separate debate.)
2021-03-21 18:44:22 +08:00
David Nadlinger c1733eef49 compiler: Fix crash with try/finally and stack-return function calls
The previous code could have never worked as-is, as the result slot
went unused, and it tried to append the load instruction to the
block just terminated with the invoke.

GitHub: Fixes #1506, #1531.
2021-03-21 18:44:14 +08:00
David Nadlinger 9e3b6faceb compiler: Map host numpy.bool_ values to TBool
Since we don't implement any integer-like operations for TBool
(addition, bitwise not, etc.), TBool is currently neither
strictly equivalent to builtin bool nor numpy.bool_, but through
very obvious compiler errors (operation not supported) rather than
silently different runtime behaviour.

Just mapping both to TBool thus is a huge improvement over the
current behaviour (where numpy.False_ is a true-like object). In
the future, we could still implement more operations for TBool,
presumably following numpy.bool_ rather than the builtin type,
just like builtin integers get translated to the numpy-like
TInt{32,64}.

GitHub: Fixes #1275.
2021-03-21 18:44:05 +08:00
David Nadlinger 01352236ee compiler: Fix type inference for "ternary" if expressions
Previously, any type would be accepted for the test expression,
leading to internal errors in the code generator if the passed
value wasn't in fact a bool.
2021-03-21 18:43:56 +08:00
David Nadlinger ca6db87895 coredevice: Fix RPC typing for bool lists/arrays
GitHub: Fixes #1635.
2021-03-20 21:33:03 +08:00
David Nadlinger c1413a9945 compiler: Change type inference rules for empty array() calls
array([...]), the constructor for NumPy arrays, currently has the
status of some weird kind of macro in ARTIQ Python, as it needs
to determine the number of dimensions in the resulting array
type, which is a fixed type parameter on which inference cannot
be performed.

This leads to an ambiguity for empty lists, which could contain
elements of arbitrary type, including other lists (which would
add to the number of dimensions).

Previously, I had chosen to make array([]) to be of completely
indeterminate type for this reason. However, this is different
to how the call behaves in host NumPy, where this is a well-formed
call creating an empty 1D array (or 2D for array([[], []]), etc.).

This commit adds special matching for (recursive lists of) empty
ListT AST nodes to treat them as scalar dimensions, with the
element type still unknown.

This also happens to fix type inference for embedding empty 1D
NumPy arrays from host object attributes, although multi-dimensional
arrays will still require work (see GitHub #1633).

GitHub: Fixes #1626.
2021-03-15 09:35:03 +08:00
David Nadlinger 925014689e compiler: Properly implement NumPy array slicing
Strided slicing of one-dimensional arrays (i.e. with non-trivial
steps) might have previously been working, but would have had
different semantics, as all slices were copies rather than a view
into the original data.

Fixing this in the future will require adding support for an index
stride field/tuple to our array representation (and all the
associated indexing logic).

GitHub: Fixes #1627.
2021-03-15 09:34:50 +08:00
David Nadlinger 8a892af244 compiler: Fix type inference in slice expressions
This was a long-standing issue affecting both lists and
the new NumPy array implementation, just caused by the
generic inference passes not being run on the slice
subexpressions (and thus e.g. ints not being monomorphized).

GitHub: Fixes #1632.
2021-03-15 09:34:41 +08:00
David Nadlinger 5dcd73107c compiler: Linguistically untangle comment [nfc] 2021-03-15 09:34:34 +08:00
Sebastien Bourdeauducq c22482787e sayma_amc: fix syntax 2021-02-17 17:45:54 +08:00
Sebastien Bourdeauducq cdd27249a2 doc: update URLs for stable channel 2021-02-17 16:12:20 +08:00
Sebastien Bourdeauducq 6861d3ab33 remove WRPLL 2021-02-17 16:09:51 +08:00
Sebastien Bourdeauducq d180a1b3af remove beta marker 2021-02-17 15:52:38 +08:00
Harry Ho d74cd24d89 sayma_amc: fix JDCGPattern data for 1Gsps 2020-12-19 17:10:11 +08:00
Harry Ho 400af2c582 sayma: use QPLL for 1GSPS JESD204B TX
* requires jesd204b changes as in https://github.com/HarryMakes/jesd204b/tree/gth
2020-12-19 17:10:11 +08:00
Harry Ho b5405dfad6 jdcg: STPL tests now perform after DAC initialization 2020-12-19 17:10:11 +08:00
Sebastien Bourdeauducq 433c3bb8f9 sayma: 1GSPS WIP 2020-08-29 19:17:11 +08:00
93 changed files with 1312 additions and 2715 deletions

0
BETA
View File

View File

@ -188,17 +188,27 @@ class FilesDock(QtWidgets.QDockWidget):
except: except:
logger.warning("unable to read metadata from %s", logger.warning("unable to read metadata from %s",
info.filePath(), exc_info=True) info.filePath(), exc_info=True)
rd = dict()
rd = {}
if "archive" in f: if "archive" in f:
rd = {k: (True, v[()]) for k, v in f["archive"].items()} def visitor(k, v):
if "datasets" in f: if isinstance(v, h5py.Dataset):
for k, v in f["datasets"].items():
if k in rd:
logger.warning("dataset '%s' is both in archive and "
"outputs", k)
rd[k] = (True, v[()]) rd[k] = (True, v[()])
if rd:
f["archive"].visititems(visitor)
if "datasets" in f:
def visitor(k, v):
if isinstance(v, h5py.Dataset):
if k in rd:
logger.warning("dataset '%s' is both in archive "
"and outputs", k)
rd[k] = (True, v[()])
f["datasets"].visititems(visitor)
self.datasets.init(rd) self.datasets.init(rd)
self.dataset_changed.emit(info.filePath()) self.dataset_changed.emit(info.filePath())
def list_activated(self, idx): def list_activated(self, idx):

View File

@ -315,6 +315,9 @@ def is_iterable(typ):
return is_listish(typ) or is_range(typ) return is_listish(typ) or is_range(typ)
def get_iterable_elt(typ): def get_iterable_elt(typ):
# TODO: Arrays count as listish, but this returns the innermost element type for
# 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): if is_str(typ) or is_bytes(typ) or is_bytearray(typ):
return TInt(types.TValue(8)) return TInt(types.TValue(8))
elif types._is_pointer(typ) or is_iterable(typ): elif types._is_pointer(typ) or is_iterable(typ):

View File

@ -5,7 +5,7 @@ the references to the host objects and translates the functions
annotated as ``@kernel`` when they are referenced. annotated as ``@kernel`` when they are referenced.
""" """
import sys, os, re, linecache, inspect, textwrap, types as pytypes, numpy import os, re, linecache, inspect, textwrap, types as pytypes, numpy
from collections import OrderedDict, defaultdict from collections import OrderedDict, defaultdict
from pythonparser import ast, algorithm, source, diagnostic, parse_buffer from pythonparser import ast, algorithm, source, diagnostic, parse_buffer
@ -45,7 +45,14 @@ class EmbeddingMap:
self.object_forward_map = {} self.object_forward_map = {}
self.object_reverse_map = {} self.object_reverse_map = {}
self.module_map = {} self.module_map = {}
# type_map connects the host Python `type` to the pair of associated
# `(TInstance, TConstructor)`s. The `used_…_names` sets cache the
# respective `.name`s for O(1) collision avoidance.
self.type_map = {} self.type_map = {}
self.used_instance_type_names = set()
self.used_constructor_type_names = set()
self.function_map = {} self.function_map = {}
# Modules # Modules
@ -60,16 +67,6 @@ class EmbeddingMap:
# Types # Types
def store_type(self, host_type, instance_type, constructor_type): def store_type(self, host_type, instance_type, constructor_type):
self._rename_type(instance_type)
self.type_map[host_type] = (instance_type, constructor_type)
def retrieve_type(self, host_type):
return self.type_map[host_type]
def has_type(self, host_type):
return host_type in self.type_map
def _rename_type(self, new_instance_type):
# Generally, user-defined types that have exact same name (which is to say, classes # Generally, user-defined types that have exact same name (which is to say, classes
# defined inside functions) do not pose a problem to the compiler. The two places which # defined inside functions) do not pose a problem to the compiler. The two places which
# cannot handle this are: # cannot handle this are:
@ -78,12 +75,29 @@ class EmbeddingMap:
# Since handling #2 requires renaming on ARTIQ side anyway, it's more straightforward # Since handling #2 requires renaming on ARTIQ side anyway, it's more straightforward
# to do it once when embedding (since non-embedded code cannot define classes in # to do it once when embedding (since non-embedded code cannot define classes in
# functions). Also, easier to debug. # functions). Also, easier to debug.
n = 0 suffix = 0
for host_type in self.type_map: new_instance_name = instance_type.name
instance_type, constructor_type = self.type_map[host_type] new_constructor_name = constructor_type.name
if instance_type.name == new_instance_type.name: while True:
n += 1 if (new_instance_name not in self.used_instance_type_names
new_instance_type.name = "{}.{}".format(new_instance_type.name, n) and new_constructor_name not in self.used_constructor_type_names):
break
suffix += 1
new_instance_name = f"{instance_type.name}.{suffix}"
new_constructor_name = f"{constructor_type.name}.{suffix}"
self.used_instance_type_names.add(new_instance_name)
instance_type.name = new_instance_name
self.used_constructor_type_names.add(new_constructor_name)
constructor_type.name = new_constructor_name
self.type_map[host_type] = (instance_type, constructor_type)
def retrieve_type(self, host_type):
return self.type_map[host_type]
def has_type(self, host_type):
return host_type in self.type_map
def attribute_count(self): def attribute_count(self):
count = 0 count = 0
@ -162,14 +176,15 @@ class ASTSynthesizer:
typ = builtins.TNone() typ = builtins.TNone()
return asttyped.NameConstantT(value=value, type=typ, return asttyped.NameConstantT(value=value, type=typ,
loc=self._add(repr(value))) loc=self._add(repr(value)))
elif value is True or value is False: elif isinstance(value, (bool, numpy.bool_)):
typ = builtins.TBool() typ = builtins.TBool()
return asttyped.NameConstantT(value=value, type=typ, coerced = bool(value)
loc=self._add(repr(value))) return asttyped.NameConstantT(value=coerced, type=typ,
elif value is numpy.float: loc=self._add(repr(coerced)))
elif value is float:
typ = builtins.fn_float() typ = builtins.fn_float()
return asttyped.NameConstantT(value=None, type=typ, return asttyped.NameConstantT(value=None, type=typ,
loc=self._add("numpy.float")) loc=self._add("float"))
elif value is numpy.int32: elif value is numpy.int32:
typ = builtins.fn_int32() typ = builtins.fn_int32()
return asttyped.NameConstantT(value=None, type=typ, return asttyped.NameConstantT(value=None, type=typ,
@ -446,7 +461,7 @@ class StitchingASTTypedRewriter(ASTTypedRewriter):
node = asttyped.QuotedFunctionDefT( node = asttyped.QuotedFunctionDefT(
typing_env=extractor.typing_env, globals_in_scope=extractor.global_, typing_env=extractor.typing_env, globals_in_scope=extractor.global_,
signature_type=types.TVar(), return_type=types.TVar(), signature_type=types.TVar(), return_type=types.TVar(),
name=node.name, args=node.args, returns=node.returns, name=node.name, args=node.args, returns=None,
body=node.body, decorator_list=node.decorator_list, body=node.body, decorator_list=node.decorator_list,
keyword_loc=node.keyword_loc, name_loc=node.name_loc, 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, arrow_loc=node.arrow_loc, colon_loc=node.colon_loc, at_locs=node.at_locs,
@ -522,7 +537,7 @@ class StitchingInferencer(Inferencer):
self.engine.process(diag) self.engine.process(diag)
return return
# Figure out what ARTIQ type does the value of the attribute have. # Figure out the ARTIQ type of the value of the attribute.
# We do this by quoting it, as if to serialize. This has some # We do this by quoting it, as if to serialize. This has some
# overhead (i.e. synthesizing a source buffer), but has the advantage # overhead (i.e. synthesizing a source buffer), but has the advantage
# of having the host-to-ARTIQ mapping code in only one place and # of having the host-to-ARTIQ mapping code in only one place and
@ -902,13 +917,11 @@ class Stitcher:
# Parse. # Parse.
source_buffer = source.Buffer(source_code, filename, first_line) source_buffer = source.Buffer(source_code, filename, first_line)
lexer = source_lexer.Lexer(source_buffer, version=sys.version_info[0:2], lexer = source_lexer.Lexer(source_buffer, version=(3, 6), diagnostic_engine=self.engine)
diagnostic_engine=self.engine)
lexer.indent = [(initial_indent, lexer.indent = [(initial_indent,
source.Range(source_buffer, 0, len(initial_whitespace)), source.Range(source_buffer, 0, len(initial_whitespace)),
initial_whitespace)] initial_whitespace)]
parser = source_parser.Parser(lexer, version=sys.version_info[0:2], parser = source_parser.Parser(lexer, version=(3, 6), diagnostic_engine=self.engine)
diagnostic_engine=self.engine)
function_node = parser.file_input().body[0] function_node = parser.file_input().body[0]
# Mangle the name, since we put everything into a single module. # Mangle the name, since we put everything into a single module.
@ -948,6 +961,31 @@ class Stitcher:
if annot is None: if annot is None:
annot = builtins.TNone() annot = builtins.TNone()
if isinstance(function, SpecializedFunction):
host_function = function.host_function
else:
host_function = function
if hasattr(host_function, 'artiq_embedded'):
embedded_function = host_function.artiq_embedded.function
else:
embedded_function = host_function
if isinstance(embedded_function, str):
embedded_function = host_function
if isinstance(annot, str):
try:
annot = eval(annot, embedded_function.__globals__)
except Exception:
diag = diagnostic.Diagnostic(
"error",
"type annotation for {kind}, {annot}, cannot be evaluated",
{"kind": kind, "annot": repr(annot)},
self._function_loc(function),
notes=self._call_site_note(call_loc, fn_kind))
self.engine.process(diag)
if not isinstance(annot, types.Type): if not isinstance(annot, types.Type):
diag = diagnostic.Diagnostic("error", diag = diagnostic.Diagnostic("error",
"type annotation for {kind}, '{annot}', is not an ARTIQ type", "type annotation for {kind}, '{annot}', is not an ARTIQ type",

View File

@ -1116,7 +1116,11 @@ class ARTIQIRGenerator(algorithm.Visitor):
_readable_name(index)))) _readable_name(index))))
if self.current_assign is None: if self.current_assign is None:
return indexed return indexed
else: # Slice else:
# This is a slice. The endpoint checking logic is the same for both lists
# and NumPy arrays, but the actual implementations differ while slices of
# built-in lists are always copies in Python, they are views sharing the
# same backing storage in NumPy.
length = self.iterable_len(value, node.slice.type) length = self.iterable_len(value, node.slice.type)
if node.slice.lower is not None: if node.slice.lower is not None:
@ -1141,6 +1145,42 @@ class ARTIQIRGenerator(algorithm.Visitor):
mapped_stop_index = self._map_index(length, stop_index, one_past_the_end=True, mapped_stop_index = self._map_index(length, stop_index, one_past_the_end=True,
loc=node.begin_loc) loc=node.begin_loc)
if builtins.is_array(node.type):
# To implement strided slicing with the proper NumPy reference
# semantics, the pointer/length array representation will need to be
# extended by another field to hold a variable stride.
assert node.slice.step is None, (
"array slices with non-trivial step "
"should have been disallowed during type inference")
# One-dimensionally slicing an array only affects the outermost
# dimension.
shape = self.append(ir.GetAttr(value, "shape"))
lengths = [
self.append(ir.GetAttr(shape, i))
for i in range(len(shape.type.elts))
]
# Compute outermost length zero for "backwards" indices.
raw_len = self.append(
ir.Arith(ast.Sub(loc=None), mapped_stop_index, mapped_start_index))
is_neg_len = self.append(
ir.Compare(ast.Lt(loc=None), raw_len, ir.Constant(0, raw_len.type)))
outer_len = self.append(
ir.Select(is_neg_len, ir.Constant(0, raw_len.type), raw_len))
new_shape = self._make_array_shape([outer_len] + lengths[1:])
# Offset buffer pointer by start index (times stride for inner dims).
stride = reduce(
lambda l, r: self.append(ir.Arith(ast.Mult(loc=None), l, r)),
lengths[1:], ir.Constant(1, lengths[0].type))
offset = self.append(
ir.Arith(ast.Mult(loc=None), stride, mapped_start_index))
buffer = self.append(ir.GetAttr(value, "buffer"))
new_buffer = self.append(ir.Offset(buffer, offset))
return self.append(ir.Alloc([new_buffer, new_shape], node.type))
else:
if node.slice.step is not None: if node.slice.step is not None:
try: try:
old_assign, self.current_assign = self.current_assign, None old_assign, self.current_assign = self.current_assign, None

View File

@ -8,6 +8,28 @@ from .. import asttyped, types, builtins
from .typedtree_printer import TypedtreePrinter from .typedtree_printer import TypedtreePrinter
def is_nested_empty_list(node):
"""If the passed AST node is an empty list, or a regularly nested list thereof,
returns the number of nesting layers, or ``None`` otherwise.
For instance, ``is_nested_empty_list([]) == 1`` and
``is_nested_empty_list([[], []]) == 2``, but
``is_nested_empty_list([[[]], []]) == None`` as the number of nesting layers doesn't
match.
"""
if not isinstance(node, ast.List):
return None
if not node.elts:
return 1
result = is_nested_empty_list(node.elts[0])
if result is None:
return None
for elt in node.elts[:1]:
if result != is_nested_empty_list(elt):
return None
return result + 1
class Inferencer(algorithm.Visitor): class Inferencer(algorithm.Visitor):
""" """
:class:`Inferencer` infers types by recursively applying the unification :class:`Inferencer` infers types by recursively applying the unification
@ -216,6 +238,7 @@ class Inferencer(algorithm.Visitor):
value.loc, None) value.loc, None)
def visit_SliceT(self, node): def visit_SliceT(self, node):
self.generic_visit(node)
if (node.lower, node.upper, node.step) == (None, None, None): if (node.lower, node.upper, node.step) == (None, None, None):
self._unify(node.type, builtins.TInt32(), self._unify(node.type, builtins.TInt32(),
node.loc, None) node.loc, None)
@ -268,12 +291,21 @@ class Inferencer(algorithm.Visitor):
else: else:
self._unify_iterable(element=node, collection=node.value) self._unify_iterable(element=node, collection=node.value)
elif isinstance(node.slice, ast.Slice): elif isinstance(node.slice, ast.Slice):
if builtins.is_array(node.value.type):
if node.slice.step is not None:
diag = diagnostic.Diagnostic(
"error",
"strided slicing not yet supported for NumPy arrays", {},
node.slice.step.loc, [])
self.engine.process(diag)
return
self._unify(node.type, node.value.type, node.loc, node.value.loc) self._unify(node.type, node.value.type, node.loc, node.value.loc)
else: # ExtSlice else: # ExtSlice
pass # error emitted above pass # error emitted above
def visit_IfExpT(self, node): def visit_IfExpT(self, node):
self.generic_visit(node) self.generic_visit(node)
self._unify(node.test.type, builtins.TBool(), node.test.loc, None)
self._unify(node.body.type, node.orelse.type, self._unify(node.body.type, node.orelse.type,
node.body.loc, node.orelse.loc) node.body.loc, node.orelse.loc)
self._unify(node.type, node.body.type, self._unify(node.type, node.body.type,
@ -882,21 +914,38 @@ class Inferencer(algorithm.Visitor):
if len(node.args) == 1 and keywords_acceptable: if len(node.args) == 1 and keywords_acceptable:
arg, = node.args arg, = node.args
num_empty_dims = is_nested_empty_list(arg)
if num_empty_dims is not None:
# As a special case, following the behaviour of numpy.array (and
# repr() on ndarrays), consider empty lists to be exactly of the
# number of dimensions given, instead of potentially containing an
# unknown number of extra dimensions.
num_dims = num_empty_dims
# The ultimate element type will be TVar initially, but we might be
# able to resolve it from context.
elt = arg.type
for _ in range(num_dims):
assert builtins.is_list(elt)
elt = elt.find()["elt"]
else:
# In the absence of any other information (there currently isn't a way # In the absence of any other information (there currently isn't a way
# to specify any), assume that all iterables are expandable into a # to specify any), assume that all iterables are expandable into a
# (runtime-checked) rectangular array of the innermost element type. # (runtime-checked) rectangular array of the innermost element type.
elt = arg.type elt = arg.type
num_dims = 0 num_dims = 0
result_dims = (node.type.find()["num_dims"].value expected_dims = (node.type.find()["num_dims"].value
if builtins.is_array(node.type) else -1) if builtins.is_array(node.type) else -1)
while True: while True:
if num_dims == result_dims: if num_dims == expected_dims:
# If we already know the number of dimensions of the result, # If we already know the number of dimensions of the result,
# stop so we can disambiguate the (innermost) element type of # stop so we can disambiguate the (innermost) element type of
# the argument if it is still unknown (e.g. empty array). # the argument if it is still unknown.
break break
if types.is_var(elt): if types.is_var(elt):
return # undetermined yet # Can't make progress here because we don't know how many more
# dimensions might be "hidden" inside.
return
if not builtins.is_iterable(elt) or builtins.is_str(elt): if not builtins.is_iterable(elt) or builtins.is_str(elt):
break break
if builtins.is_array(elt): if builtins.is_array(elt):

View File

@ -331,8 +331,8 @@ class LLVMIRGenerator:
else: else:
value = const.value value = const.value
llptr = self.llstr_of_str(const.value, linkage="private", unnamed_addr=True) llptr = self.llstr_of_str(value, linkage="private", unnamed_addr=True)
lllen = ll.Constant(lli32, len(const.value)) lllen = ll.Constant(lli32, len(value))
return ll.Constant(llty, (llptr, lllen)) return ll.Constant(llty, (llptr, lllen))
else: else:
assert False assert False
@ -1328,13 +1328,13 @@ class LLVMIRGenerator:
self.engine.process(diag) self.engine.process(diag)
tag += ir.rpc_tag(fun_type.ret, ret_error_handler) tag += ir.rpc_tag(fun_type.ret, ret_error_handler)
llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [],
name="rpc.stack")
lltag = self.llconst_of_const(ir.Constant(tag, builtins.TStr())) lltag = self.llconst_of_const(ir.Constant(tag, builtins.TStr()))
lltagptr = self.llbuilder.alloca(lltag.type) lltagptr = self.llbuilder.alloca(lltag.type)
self.llbuilder.store(lltag, lltagptr) self.llbuilder.store(lltag, lltagptr)
llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [],
name="rpc.stack")
llargs = self.llbuilder.alloca(llptr, ll.Constant(lli32, len(args)), llargs = self.llbuilder.alloca(llptr, ll.Constant(lli32, len(args)),
name="rpc.args") name="rpc.args")
for index, arg in enumerate(args): for index, arg in enumerate(args):
@ -1474,19 +1474,22 @@ class LLVMIRGenerator:
llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), []) llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [])
llresultslot = self.llbuilder.alloca(llfun.type.pointee.args[0].pointee) llresultslot = self.llbuilder.alloca(llfun.type.pointee.args[0].pointee)
llcall = self.llbuilder.invoke(llfun, llargs, llnormalblock, llunwindblock, llcall = self.llbuilder.invoke(llfun, [llresultslot] + llargs,
name=insn.name) llnormalblock, llunwindblock, name=insn.name)
self.llbuilder.position_at_start(llnormalblock)
llresult = self.llbuilder.load(llresultslot) llresult = self.llbuilder.load(llresultslot)
self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr]) self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr])
else: else:
llcall = self.llbuilder.invoke(llfun, llargs, llnormalblock, llunwindblock, llcall = self.llbuilder.invoke(llfun, llargs, llnormalblock, llunwindblock,
name=insn.name) name=insn.name)
llresult = llcall
# The !tbaa metadata is not legal to use with the invoke instruction, # The !tbaa metadata is not legal to use with the invoke instruction,
# so unlike process_Call, we do not set it here. # so unlike process_Call, we do not set it here.
return llcall return llresult
def _quote_listish_to_llglobal(self, value, elt_type, path, kind_name): def _quote_listish_to_llglobal(self, value, elt_type, path, kind_name):
llelts = [self._quote(value[i], elt_type, lambda: path() + [str(i)]) llelts = [self._quote(value[i], elt_type, lambda: path() + [str(i)])
@ -1558,7 +1561,8 @@ class LLVMIRGenerator:
return ll.Constant.literal_struct([]) return ll.Constant.literal_struct([])
elif builtins.is_bool(typ): elif builtins.is_bool(typ):
assert value in (True, False), fail_msg assert value in (True, False), fail_msg
return ll.Constant(llty, value) # Explicitly cast to bool to handle numpy.bool_.
return ll.Constant(llty, bool(value))
elif builtins.is_int(typ): elif builtins.is_int(typ):
assert isinstance(value, (int, numpy.int32, numpy.int64)), fail_msg assert isinstance(value, (int, numpy.int32, numpy.int64)), fail_msg
return ll.Constant(llty, int(value)) return ll.Constant(llty, int(value))

View File

@ -3,6 +3,7 @@ The :mod:`types` module contains the classes describing the types
in :mod:`asttyped`. in :mod:`asttyped`.
""" """
import builtins
import string import string
from collections import OrderedDict from collections import OrderedDict
from . import iodelay from . import iodelay
@ -97,6 +98,8 @@ class TVar(Type):
return self.find().fold(accum, fn) return self.find().fold(accum, fn)
def __repr__(self): def __repr__(self):
if getattr(builtins, "__in_sphinx__", False):
return str(self)
if self.parent is self: if self.parent is self:
return "<artiq.compiler.types.TVar %d>" % id(self) return "<artiq.compiler.types.TVar %d>" % id(self)
else: else:
@ -143,6 +146,8 @@ class TMono(Type):
return fn(accum, self) return fn(accum, self)
def __repr__(self): def __repr__(self):
if getattr(builtins, "__in_sphinx__", False):
return str(self)
return "artiq.compiler.types.TMono(%s, %s)" % (repr(self.name), repr(self.params)) return "artiq.compiler.types.TMono(%s, %s)" % (repr(self.name), repr(self.params))
def __getitem__(self, param): def __getitem__(self, param):
@ -191,6 +196,8 @@ class TTuple(Type):
return fn(accum, self) return fn(accum, self)
def __repr__(self): def __repr__(self):
if getattr(builtins, "__in_sphinx__", False):
return str(self)
return "artiq.compiler.types.TTuple(%s)" % repr(self.elts) return "artiq.compiler.types.TTuple(%s)" % repr(self.elts)
def __eq__(self, other): def __eq__(self, other):
@ -269,6 +276,8 @@ class TFunction(Type):
return fn(accum, self) return fn(accum, self)
def __repr__(self): def __repr__(self):
if getattr(builtins, "__in_sphinx__", False):
return str(self)
return "artiq.compiler.types.TFunction({}, {}, {})".format( return "artiq.compiler.types.TFunction({}, {}, {})".format(
repr(self.args), repr(self.optargs), repr(self.ret)) repr(self.args), repr(self.optargs), repr(self.ret))
@ -362,6 +371,8 @@ class TRPC(Type):
return fn(accum, self) return fn(accum, self)
def __repr__(self): def __repr__(self):
if getattr(builtins, "__in_sphinx__", False):
return str(self)
return "artiq.compiler.types.TRPC({})".format(repr(self.ret)) return "artiq.compiler.types.TRPC({})".format(repr(self.ret))
def __eq__(self, other): def __eq__(self, other):
@ -399,6 +410,8 @@ class TBuiltin(Type):
return fn(accum, self) return fn(accum, self)
def __repr__(self): def __repr__(self):
if getattr(builtins, "__in_sphinx__", False):
return str(self)
return "artiq.compiler.types.{}({})".format(type(self).__name__, repr(self.name)) return "artiq.compiler.types.{}({})".format(type(self).__name__, repr(self.name))
def __eq__(self, other): def __eq__(self, other):
@ -459,6 +472,8 @@ class TInstance(TMono):
self.constant_attributes = set() self.constant_attributes = set()
def __repr__(self): def __repr__(self):
if getattr(builtins, "__in_sphinx__", False):
return str(self)
return "artiq.compiler.types.TInstance({}, {})".format( return "artiq.compiler.types.TInstance({}, {})".format(
repr(self.name), repr(self.attributes)) repr(self.name), repr(self.attributes))
@ -474,6 +489,8 @@ class TModule(TMono):
self.constant_attributes = set() self.constant_attributes = set()
def __repr__(self): def __repr__(self):
if getattr(builtins, "__in_sphinx__", False):
return str(self)
return "artiq.compiler.types.TModule({}, {})".format( return "artiq.compiler.types.TModule({}, {})".format(
repr(self.name), repr(self.attributes)) repr(self.name), repr(self.attributes))
@ -513,6 +530,8 @@ class TValue(Type):
return fn(accum, self) return fn(accum, self)
def __repr__(self): def __repr__(self):
if getattr(builtins, "__in_sphinx__", False):
return str(self)
return "artiq.compiler.types.TValue(%s)" % repr(self.value) return "artiq.compiler.types.TValue(%s)" % repr(self.value)
def __eq__(self, other): def __eq__(self, other):
@ -571,6 +590,8 @@ class TDelay(Type):
return not (self == other) return not (self == other)
def __repr__(self): def __repr__(self):
if getattr(builtins, "__in_sphinx__", False):
return str(self)
if self.duration is None: if self.duration is None:
return "<{}.TIndeterminateDelay>".format(__name__) return "<{}.TIndeterminateDelay>".format(__name__)
elif self.cause is None: elif self.cause is None:

View File

@ -233,7 +233,7 @@ class AD53xx:
def write_gain_mu(self, channel, gain=0xffff): def write_gain_mu(self, channel, gain=0xffff):
"""Program the gain register for a DAC channel. """Program the gain register for a DAC channel.
The DAC output is not updated until LDAC is pulsed (see :meth load:). The DAC output is not updated until LDAC is pulsed (see :meth:`load`).
This method advances the timeline by the duration of one SPI transfer. This method advances the timeline by the duration of one SPI transfer.
:param gain: 16-bit gain register value (default: 0xffff) :param gain: 16-bit gain register value (default: 0xffff)
@ -245,7 +245,7 @@ class AD53xx:
def write_offset_mu(self, channel, offset=0x8000): def write_offset_mu(self, channel, offset=0x8000):
"""Program the offset register for a DAC channel. """Program the offset register for a DAC channel.
The DAC output is not updated until LDAC is pulsed (see :meth load:). The DAC output is not updated until LDAC is pulsed (see :meth:`load`).
This method advances the timeline by the duration of one SPI transfer. This method advances the timeline by the duration of one SPI transfer.
:param offset: 16-bit offset register value (default: 0x8000) :param offset: 16-bit offset register value (default: 0x8000)
@ -258,7 +258,7 @@ class AD53xx:
"""Program the DAC offset voltage for a channel. """Program the DAC offset voltage for a channel.
An offset of +V can be used to trim out a DAC offset error of -V. An offset of +V can be used to trim out a DAC offset error of -V.
The DAC output is not updated until LDAC is pulsed (see :meth load:). The DAC output is not updated until LDAC is pulsed (see :meth:`load`).
This method advances the timeline by the duration of one SPI transfer. This method advances the timeline by the duration of one SPI transfer.
:param voltage: the offset voltage :param voltage: the offset voltage
@ -270,7 +270,7 @@ class AD53xx:
def write_dac_mu(self, channel, value): def write_dac_mu(self, channel, value):
"""Program the DAC input register for a channel. """Program the DAC input register for a channel.
The DAC output is not updated until LDAC is pulsed (see :meth load:). The DAC output is not updated until LDAC is pulsed (see :meth:`load`).
This method advances the timeline by the duration of one SPI transfer. This method advances the timeline by the duration of one SPI transfer.
""" """
self.bus.write( self.bus.write(
@ -280,7 +280,7 @@ class AD53xx:
def write_dac(self, channel, voltage): def write_dac(self, channel, voltage):
"""Program the DAC output voltage for a channel. """Program the DAC output voltage for a channel.
The DAC output is not updated until LDAC is pulsed (see :meth load:). The DAC output is not updated until LDAC is pulsed (see :meth:`load`).
This method advances the timeline by the duration of one SPI transfer. This method advances the timeline by the duration of one SPI transfer.
""" """
self.write_dac_mu(channel, voltage_to_mu(voltage, self.offset_dacs, self.write_dac_mu(channel, voltage_to_mu(voltage, self.offset_dacs,
@ -313,7 +313,7 @@ class AD53xx:
If no LDAC device was defined, the LDAC pulse is skipped. If no LDAC device was defined, the LDAC pulse is skipped.
See :meth load:. See :meth:`load`.
:param values: list of DAC values to program :param values: list of DAC values to program
:param channels: list of DAC channels to program. If not specified, :param channels: list of DAC channels to program. If not specified,
@ -355,7 +355,7 @@ class AD53xx:
""" Two-point calibration of a DAC channel. """ Two-point calibration of a DAC channel.
Programs the offset and gain register to trim out DAC errors. Does not Programs the offset and gain register to trim out DAC errors. Does not
take effect until LDAC is pulsed (see :meth load:). take effect until LDAC is pulsed (see :meth:`load`).
Calibration consists of measuring the DAC output voltage for a channel Calibration consists of measuring the DAC output voltage for a channel
with the DAC set to zero-scale (0x0000) and full-scale (0xffff). with the DAC set to zero-scale (0x0000) and full-scale (0xffff).

View File

@ -134,12 +134,14 @@ class AD9910:
from a I2C EEPROM; in which case, `sync_delay_seed` must be set to the from a I2C EEPROM; in which case, `sync_delay_seed` must be set to the
same string value. same string value.
""" """
kernel_invariants = {"chip_select", "cpld", "core", "bus",
"ftw_per_hz", "sysclk_per_mu"}
def __init__(self, dmgr, chip_select, cpld_device, sw_device=None, def __init__(self, dmgr, chip_select, cpld_device, sw_device=None,
pll_n=40, pll_cp=7, pll_vco=5, sync_delay_seed=-1, pll_n=40, pll_cp=7, pll_vco=5, sync_delay_seed=-1,
io_update_delay=0, pll_en=1): io_update_delay=0, pll_en=1):
self.kernel_invariants = {"cpld", "core", "bus", "chip_select",
"pll_en", "pll_n", "pll_vco", "pll_cp",
"ftw_per_hz", "sysclk_per_mu", "sysclk",
"sync_data"}
self.cpld = dmgr.get(cpld_device) self.cpld = dmgr.get(cpld_device)
self.core = self.cpld.core self.core = self.cpld.core
self.bus = self.cpld.bus self.bus = self.cpld.bus

View File

@ -26,10 +26,11 @@ class AD9912:
is the reference clock divider (both set in the parent Urukul CPLD is the reference clock divider (both set in the parent Urukul CPLD
instance). instance).
""" """
kernel_invariants = {"chip_select", "cpld", "core", "bus", "ftw_per_hz"}
def __init__(self, dmgr, chip_select, cpld_device, sw_device=None, def __init__(self, dmgr, chip_select, cpld_device, sw_device=None,
pll_n=10): pll_n=10):
self.kernel_invariants = {"cpld", "core", "bus", "chip_select",
"pll_n", "ftw_per_hz"}
self.cpld = dmgr.get(cpld_device) self.cpld = dmgr.get(cpld_device)
self.core = self.cpld.core self.core = self.cpld.core
self.bus = self.cpld.bus self.bus = self.cpld.bus

View File

@ -102,15 +102,15 @@ def decode_dump(data):
# messages are big endian # messages are big endian
parts = struct.unpack(endian + "IQbbb", data[:15]) parts = struct.unpack(endian + "IQbbb", data[:15])
(sent_bytes, total_byte_count, (sent_bytes, total_byte_count,
error_occured, log_channel, dds_onehot_sel) = parts error_occurred, log_channel, dds_onehot_sel) = parts
expected_len = sent_bytes + 15 expected_len = sent_bytes + 15
if expected_len != len(data): if expected_len != len(data):
raise ValueError("analyzer dump has incorrect length " raise ValueError("analyzer dump has incorrect length "
"(got {}, expected {})".format( "(got {}, expected {})".format(
len(data), expected_len)) len(data), expected_len))
if error_occured: if error_occurred:
logger.warning("error occured within the analyzer, " logger.warning("error occurred within the analyzer, "
"data may be corrupted") "data may be corrupted")
if total_byte_count > sent_bytes: if total_byte_count > sent_bytes:
logger.info("analyzer ring buffer has wrapped %d times", logger.info("analyzer ring buffer has wrapped %d times",

View File

@ -66,13 +66,13 @@ def _receive_list(kernel, embedding_map):
tag = chr(kernel._read_int8()) tag = chr(kernel._read_int8())
if tag == "b": if tag == "b":
buffer = kernel._read(length) buffer = kernel._read(length)
return list(buffer) return list(struct.unpack(kernel.endian + "%s?" % length, buffer))
elif tag == "i": elif tag == "i":
buffer = kernel._read(4 * length) buffer = kernel._read(4 * length)
return list(struct.unpack(kernel.endian + "%sl" % length, buffer)) return list(struct.unpack(kernel.endian + "%sl" % length, buffer))
elif tag == "I": elif tag == "I":
buffer = kernel._read(8 * length) buffer = kernel._read(8 * length)
return list(struct.unpack(kernel.endian + "%sq" % length, buffer)) return list(numpy.ndarray((length, ), kernel.endian + 'i8', buffer))
elif tag == "f": elif tag == "f":
buffer = kernel._read(8 * length) buffer = kernel._read(8 * length)
return list(struct.unpack(kernel.endian + "%sd" % length, buffer)) return list(struct.unpack(kernel.endian + "%sd" % length, buffer))
@ -96,7 +96,7 @@ def _receive_array(kernel, embedding_map):
length = numpy.prod(shape) length = numpy.prod(shape)
if tag == "b": if tag == "b":
buffer = kernel._read(length) buffer = kernel._read(length)
elems = numpy.ndarray((length, ), 'B', buffer) elems = numpy.ndarray((length, ), '?', buffer)
elif tag == "i": elif tag == "i":
buffer = kernel._read(4 * length) buffer = kernel._read(4 * length)
elems = numpy.ndarray((length, ), kernel.endian + 'i4', buffer) elems = numpy.ndarray((length, ), kernel.endian + 'i4', buffer)
@ -437,12 +437,12 @@ class CommKernel:
self._write_bool(value) self._write_bool(value)
elif tag == "i": elif tag == "i":
check(isinstance(value, (int, numpy.int32)) and check(isinstance(value, (int, numpy.int32)) and
(-2**31 < value < 2**31-1), (-2**31 <= value < 2**31),
lambda: "32-bit int") lambda: "32-bit int")
self._write_int32(value) self._write_int32(value)
elif tag == "I": elif tag == "I":
check(isinstance(value, (int, numpy.int32, numpy.int64)) and check(isinstance(value, (int, numpy.int32, numpy.int64)) and
(-2**63 < value < 2**63-1), (-2**63 <= value < 2**63),
lambda: "64-bit int") lambda: "64-bit int")
self._write_int64(value) self._write_int64(value)
elif tag == "f": elif tag == "f":
@ -451,8 +451,8 @@ class CommKernel:
self._write_float64(value) self._write_float64(value)
elif tag == "F": elif tag == "F":
check(isinstance(value, Fraction) and check(isinstance(value, Fraction) and
(-2**63 < value.numerator < 2**63-1) and (-2**63 <= value.numerator < 2**63) and
(-2**63 < value.denominator < 2**63-1), (-2**63 <= value.denominator < 2**63),
lambda: "64-bit Fraction") lambda: "64-bit Fraction")
self._write_int64(value.numerator) self._write_int64(value.numerator)
self._write_int64(value.denominator) self._write_int64(value.denominator)
@ -476,11 +476,19 @@ class CommKernel:
if tag_element == "b": if tag_element == "b":
self._write(bytes(value)) self._write(bytes(value))
elif tag_element == "i": elif tag_element == "i":
self._write(struct.pack(self.endian + "%sl" % try:
len(value), *value)) self._write(struct.pack(self.endian + "%sl" % len(value), *value))
except struct.error:
raise RPCReturnValueError(
"type mismatch: cannot serialize {value} as {type}".format(
value=repr(value), type="32-bit integer list"))
elif tag_element == "I": elif tag_element == "I":
self._write(struct.pack(self.endian + "%sq" % try:
len(value), *value)) self._write(struct.pack(self.endian + "%sq" % len(value), *value))
except struct.error:
raise RPCReturnValueError(
"type mismatch: cannot serialize {value} as {type}".format(
value=repr(value), type="64-bit integer list"))
elif tag_element == "f": elif tag_element == "f":
self._write(struct.pack(self.endian + "%sd" % self._write(struct.pack(self.endian + "%sd" %
len(value), *value)) len(value), *value))
@ -555,14 +563,6 @@ class CommKernel:
try: try:
result = service(*args, **kwargs) result = service(*args, **kwargs)
logger.debug("rpc service: %d %r %r = %r",
service_id, args, kwargs, result)
self._write_header(Request.RPCReply)
self._write_bytes(return_tags)
self._send_rpc_value(bytearray(return_tags),
result, result, service)
self._flush()
except RPCReturnValueError as exn: except RPCReturnValueError as exn:
raise raise
except Exception as exn: except Exception as exn:
@ -609,6 +609,14 @@ class CommKernel:
self._write_int32(-1) # column not known self._write_int32(-1) # column not known
self._write_string(function) self._write_string(function)
self._flush() self._flush()
else:
logger.debug("rpc service: %d %r %r = %r",
service_id, args, kwargs, result)
self._write_header(Request.RPCReply)
self._write_bytes(return_tags)
self._send_rpc_value(bytearray(return_tags),
result, result, service)
self._flush()
def _serve_exception(self, embedding_map, symbolizer, demangler): def _serve_exception(self, embedding_map, symbolizer, demangler):
name = self._read_string() name = self._read_string()

View File

@ -176,7 +176,7 @@ class CommMgmt:
self._write_bytes(value) self._write_bytes(value)
ty = self._read_header() ty = self._read_header()
if ty == Reply.Error: if ty == Reply.Error:
raise IOError("Flash storage is full") raise IOError("Device failed to write config. More information may be available in the log.")
elif ty != Reply.Success: elif ty != Reply.Success:
raise IOError("Incorrect reply from device: {} (expected {})". raise IOError("Incorrect reply from device: {} (expected {})".
format(ty, Reply.Success)) format(ty, Reply.Success))

View File

@ -82,12 +82,12 @@ class CommMonInj:
if not ty: if not ty:
return return
if ty == b"\x00": if ty == b"\x00":
payload = await self._reader.read(9) payload = await self._reader.readexactly(9)
channel, probe, value = struct.unpack( channel, probe, value = struct.unpack(
self.endian + "lbl", payload) self.endian + "lbl", payload)
self.monitor_cb(channel, probe, value) self.monitor_cb(channel, probe, value)
elif ty == b"\x01": elif ty == b"\x01":
payload = await self._reader.read(6) payload = await self._reader.readexactly(6)
channel, override, value = struct.unpack( channel, override, value = struct.unpack(
self.endian + "lbb", payload) self.endian + "lbb", payload)
self.injection_status_cb(channel, override, value) self.injection_status_cb(channel, override, value)

View File

@ -370,9 +370,17 @@
"default": 100e6 "default": 100e6
}, },
"clk_sel": { "clk_sel": {
"oneOf": [
{
"type": "integer", "type": "integer",
"minimum": 0, "minimum": 0,
"maximum": 3, "maximum": 3
},
{
"type": "string",
"enum": ["xo", "mmcx", "sma"]
}
],
"default": 0 "default": 0
} }
}, },

View File

@ -1,6 +1,7 @@
"""RTIO driver for the Fastino 32channel, 16 bit, 2.5 MS/s per channel, """RTIO driver for the Fastino 32channel, 16 bit, 2.5 MS/s per channel,
streaming DAC. streaming DAC.
""" """
from numpy import int32
from artiq.language.core import kernel, portable, delay from artiq.language.core import kernel, portable, delay
from artiq.coredevice.rtio import (rtio_output, rtio_output_wide, from artiq.coredevice.rtio import (rtio_output, rtio_output_wide,
@ -112,7 +113,7 @@ class Fastino:
:param voltage: Voltage in SI Volts. :param voltage: Voltage in SI Volts.
:return: DAC data word in machine units, 16 bit integer. :return: DAC data word in machine units, 16 bit integer.
""" """
data = int(round((0x8000/10.)*voltage)) + 0x8000 data = int32(round((0x8000/10.)*voltage)) + int32(0x8000)
if data < 0 or data > 0xffff: if data < 0 or data > 0xffff:
raise ValueError("DAC voltage out of bounds") raise ValueError("DAC voltage out of bounds")
return data return data
@ -129,7 +130,7 @@ class Fastino:
v = self.voltage_to_mu(voltage[i]) v = self.voltage_to_mu(voltage[i])
if i & 1: if i & 1:
v = data[i // 2] | (v << 16) v = data[i // 2] | (v << 16)
data[i // 2] = v data[i // 2] = int32(v)
@kernel @kernel
def set_dac(self, dac, voltage): def set_dac(self, dac, voltage):

View File

@ -429,8 +429,7 @@ class Phaser:
* :const:`PHASER_STA_TRF1_LD`: Quadrature upconverter 1 lock detect * :const:`PHASER_STA_TRF1_LD`: Quadrature upconverter 1 lock detect
* :const:`PHASER_STA_TERM0`: ADC channel 0 termination indicator * :const:`PHASER_STA_TERM0`: ADC channel 0 termination indicator
* :const:`PHASER_STA_TERM1`: ADC channel 1 termination indicator * :const:`PHASER_STA_TERM1`: ADC channel 1 termination indicator
* :const:`PHASER_STA_SPI_IDLE`: SPI machine is idle and data registers * :const:`PHASER_STA_SPI_IDLE`: SPI machine is idle and data registers can be read/written
can be read/written
:return: Status register :return: Status register
""" """
@ -566,7 +565,7 @@ class Phaser:
def dac_iotest(self, pattern) -> TInt32: def dac_iotest(self, pattern) -> TInt32:
"""Performs a DAC IO test according to the datasheet. """Performs a DAC IO test according to the datasheet.
:param patterm: List of four int32 containing the pattern :param pattern: List of four int32 containing the pattern
:return: Bit error mask (16 bits) :return: Bit error mask (16 bits)
""" """
if len(pattern) != 4: if len(pattern) != 4:
@ -656,10 +655,11 @@ class PhaserChannel:
* multiple oscillators (in the coredevice phy), * multiple oscillators (in the coredevice phy),
* an interpolation chain and digital upconverter (DUC) on Phaser, * an interpolation chain and digital upconverter (DUC) on Phaser,
* several channel-specific settings in the DAC: * several channel-specific settings in the DAC:
* quadrature modulation compensation QMC * quadrature modulation compensation QMC
* numerically controlled oscillator NCO or coarse mixer CMIX, * numerically controlled oscillator NCO or coarse mixer CMIX,
* the analog quadrature upconverter (in the Phaser-Upconverter hardware
variant), and * the analog quadrature upconverter (in the Phaser-Upconverter hardware variant), and
* a digitally controlled step attenuator. * a digitally controlled step attenuator.
Attributes: Attributes:

View File

@ -45,6 +45,7 @@ class AppletsCCBDock(applets.AppletsDock):
self.ccbp_group_action.setMenu(ccbp_group_menu) self.ccbp_group_action.setMenu(ccbp_group_menu)
self.table.addAction(self.ccbp_group_action) self.table.addAction(self.ccbp_group_action)
self.table.itemSelectionChanged.connect(self.update_group_ccbp_menu) self.table.itemSelectionChanged.connect(self.update_group_ccbp_menu)
self.update_group_ccbp_menu()
ccbp_global_menu = QtWidgets.QMenu() ccbp_global_menu = QtWidgets.QMenu()
actiongroup = QtWidgets.QActionGroup(self.table) actiongroup = QtWidgets.QActionGroup(self.table)

View File

@ -258,7 +258,7 @@ class _ExperimentDock(QtWidgets.QMdiSubWindow):
datetime.setDate(QtCore.QDate.currentDate()) datetime.setDate(QtCore.QDate.currentDate())
else: else:
datetime.setDateTime(QtCore.QDateTime.fromMSecsSinceEpoch( datetime.setDateTime(QtCore.QDateTime.fromMSecsSinceEpoch(
scheduling["due_date"]*1000)) int(scheduling["due_date"]*1000)))
datetime_en.setChecked(scheduling["due_date"] is not None) datetime_en.setChecked(scheduling["due_date"] is not None)
def update_datetime(dt): def update_datetime(dt):

View File

@ -226,7 +226,7 @@ def setup_from_ddb(ddb):
dds_sysclk = v["arguments"]["sysclk"] dds_sysclk = v["arguments"]["sysclk"]
widget = _WidgetDesc(k, comment, _DDSWidget, (bus_channel, channel, k)) widget = _WidgetDesc(k, comment, _DDSWidget, (bus_channel, channel, k))
description.add(widget) description.add(widget)
elif ( (v["module"] == "artiq.coredevice.ad53xx" and v["class"] == "AD53XX") elif ( (v["module"] == "artiq.coredevice.ad53xx" and v["class"] == "AD53xx")
or (v["module"] == "artiq.coredevice.zotino" and v["class"] == "Zotino")): or (v["module"] == "artiq.coredevice.zotino" and v["class"] == "Zotino")):
spi_device = v["arguments"]["spi_device"] spi_device = v["arguments"]["spi_device"]
spi_device = ddb[spi_device] spi_device = ddb[spi_device]

View File

@ -5,7 +5,7 @@ device_db = {
"type": "local", "type": "local",
"module": "artiq.coredevice.core", "module": "artiq.coredevice.core",
"class": "Core", "class": "Core",
"arguments": {"host": core_addr, "ref_period": 1/(8*150e6)} "arguments": {"host": core_addr, "ref_period": 1/(8*125e6)}
}, },
"core_log": { "core_log": {
"type": "controller", "type": "controller",

View File

@ -5,7 +5,7 @@ device_db = {
"type": "local", "type": "local",
"module": "artiq.coredevice.core", "module": "artiq.coredevice.core",
"class": "Core", "class": "Core",
"arguments": {"host": core_addr, "ref_period": 1/(8*150e6)} "arguments": {"host": core_addr, "ref_period": 1/(8*125e6)}
}, },
"core_log": { "core_log": {
"type": "controller", "type": "controller",
@ -69,7 +69,7 @@ device_db.update(
"arguments": { "arguments": {
"spi_device": "spi_urukul0", "spi_device": "spi_urukul0",
"io_update_device": "ttl_urukul0_io_update", "io_update_device": "ttl_urukul0_io_update",
"refclk": 150e6, "refclk": 125e6,
"clk_sel": 2 "clk_sel": 2
} }
} }

View File

@ -5,7 +5,7 @@ device_db = {
"type": "local", "type": "local",
"module": "artiq.coredevice.core", "module": "artiq.coredevice.core",
"class": "Core", "class": "Core",
"arguments": {"host": core_addr, "ref_period": 1/(8*150e6)} "arguments": {"host": core_addr, "ref_period": 1/(8*125e6)}
}, },
"core_log": { "core_log": {
"type": "controller", "type": "controller",

View File

@ -5,7 +5,7 @@ device_db = {
"type": "local", "type": "local",
"module": "artiq.coredevice.core", "module": "artiq.coredevice.core",
"class": "Core", "class": "Core",
"arguments": {"host": core_addr, "ref_period": 1/(8*150e6)} "arguments": {"host": core_addr, "ref_period": 1/(8*125e6)}
}, },
"core_log": { "core_log": {
"type": "controller", "type": "controller",

View File

@ -34,9 +34,9 @@ fn read(addr: u16) -> u8 {
} }
// ad9154 mode 1 // ad9154 mode 1
// linerate 5Gbps or 6Gbps // linerate 10Gbps
// deviceclock_fpga 125MHz or 150MHz // deviceclock_fpga 125MHz
// deviceclock_dac 500MHz or 600MHz // deviceclock_dac 1000MHz
struct JESDSettings { struct JESDSettings {
did: u8, did: u8,
@ -87,7 +87,7 @@ const JESD_SETTINGS: JESDSettings = JESDSettings {
np: 16, np: 16,
f: 2, f: 2,
s: 2, s: 2,
k: 16, k: 32,
cs: 0, cs: 0,
subclassv: 1, subclassv: 1,
@ -144,9 +144,7 @@ pub fn setup(dacno: u8, linerate: u64) -> Result<(), &'static str> {
write(ad9154_reg::DEVICE_CONFIG_REG_1, 0x01); // magic write(ad9154_reg::DEVICE_CONFIG_REG_1, 0x01); // magic
write(ad9154_reg::DEVICE_CONFIG_REG_2, 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, 0x01); // 2x
write(ad9154_reg::INTERP_MODE, 0x03); // 4x
write(ad9154_reg::MIX_MODE, 0); write(ad9154_reg::MIX_MODE, 0);
write(ad9154_reg::DATA_FORMAT, 0*ad9154_reg::BINARY_FORMAT); // s16 write(ad9154_reg::DATA_FORMAT, 0*ad9154_reg::BINARY_FORMAT); // s16
write(ad9154_reg::DATAPATH_CTRL, write(ad9154_reg::DATAPATH_CTRL,
@ -326,13 +324,17 @@ pub fn setup(dacno: u8, linerate: u64) -> Result<(), &'static str> {
1*ad9154_reg::EQ_POWER_MODE); 1*ad9154_reg::EQ_POWER_MODE);
write(ad9154_reg::GENERAL_JRX_CTRL_1, 1); // subclass 1 write(ad9154_reg::GENERAL_JRX_CTRL_1, 1); // subclass 1
write(ad9154_reg::LMFC_DELAY_0, 0); // LMFCDel & LMFCVar were deduced from values of DYN_LINK_LATENCY_0
write(ad9154_reg::LMFC_DELAY_1, 0); // gathered from repeated power-cycles; see datasheet (Rev. C) p.44
write(ad9154_reg::LMFC_VAR_0, 0x0a); // receive buffer delay // "Link Delay Setup Example, Without Known Delay"
write(ad9154_reg::LMFC_VAR_1, 0x0a); write(ad9154_reg::LMFC_DELAY_0, 14);
write(ad9154_reg::LMFC_DELAY_1, 14);
write(ad9154_reg::LMFC_VAR_0, 4); // receive buffer delay
write(ad9154_reg::LMFC_VAR_1, 4);
write(ad9154_reg::SYNC_ERRWINDOW, 0); // +- 1/2 DAC clock write(ad9154_reg::SYNC_ERRWINDOW, 0); // +- 1/2 DAC clock
// datasheet seems to say ENABLE and ARM should be separate steps, // datasheet seems to say ENABLE and ARM should be separate steps,
// so enable now so it can be armed in sync(). // so enable now so it can be armed in sync().
write(ad9154_reg::SPI_PAGEINDX, 0x3); // A and B dual
write(ad9154_reg::SYNC_CONTROL, write(ad9154_reg::SYNC_CONTROL,
0x1*ad9154_reg::SYNCMODE | 1*ad9154_reg::SYNCENABLE | 0x1*ad9154_reg::SYNCMODE | 1*ad9154_reg::SYNCENABLE |
0*ad9154_reg::SYNCARM | 0*ad9154_reg::SYNCCLRSTKY); 0*ad9154_reg::SYNCARM | 0*ad9154_reg::SYNCCLRSTKY);
@ -349,6 +351,28 @@ pub fn setup(dacno: u8, linerate: u64) -> Result<(), &'static str> {
write(ad9154_reg::GENERAL_JRX_CTRL_0, write(ad9154_reg::GENERAL_JRX_CTRL_0,
0x1*ad9154_reg::LINK_EN | 0*ad9154_reg::LINK_PAGE | 0x1*ad9154_reg::LINK_EN | 0*ad9154_reg::LINK_PAGE |
0*ad9154_reg::LINK_MODE | 0*ad9154_reg::CHECKSUM_MODE); 0*ad9154_reg::LINK_MODE | 0*ad9154_reg::CHECKSUM_MODE);
// JESD Checks
let jesd_checks = read(ad9154_reg::JESD_CHECKS);
if jesd_checks & ad9154_reg::ERR_DLYOVER == ad9154_reg::ERR_DLYOVER {
error!("LMFC_Delay > JESD_K Parameter")
}
if jesd_checks & ad9154_reg::ERR_WINLIMIT == ad9154_reg::ERR_WINLIMIT {
error!("Unsupported Window Limit")
}
if jesd_checks & ad9154_reg::ERR_JESDBAD == ad9154_reg::ERR_JESDBAD {
error!("Unsupported M/L/S/F Selection")
}
if jesd_checks & ad9154_reg::ERR_KUNSUPP == ad9154_reg::ERR_KUNSUPP {
error!("Unsupported K Values")
}
if jesd_checks & ad9154_reg::ERR_SUBCLASS == ad9154_reg::ERR_SUBCLASS {
error!("Unsupported SUBCLASSV Value")
}
if jesd_checks & ad9154_reg::ERR_INTSUPP == ad9154_reg::ERR_INTSUPP {
error!("Unsupported Interpolation Factor")
}
info!(" ...done"); info!(" ...done");
Ok(()) Ok(())
} }
@ -529,6 +553,7 @@ pub fn stpl(dacno: u8, m: u8, s: u8) -> Result<(), &'static str> {
pub fn sync(dacno: u8) -> Result<bool, &'static str> { pub fn sync(dacno: u8) -> Result<bool, &'static str> {
spi_setup(dacno); spi_setup(dacno);
write(ad9154_reg::SPI_PAGEINDX, 0x3); // A and B dual
write(ad9154_reg::SYNC_CONTROL, write(ad9154_reg::SYNC_CONTROL,
0x1*ad9154_reg::SYNCMODE | 1*ad9154_reg::SYNCENABLE | 0x1*ad9154_reg::SYNCMODE | 1*ad9154_reg::SYNCENABLE |
1*ad9154_reg::SYNCARM | 1*ad9154_reg::SYNCCLRSTKY); 1*ad9154_reg::SYNCARM | 1*ad9154_reg::SYNCCLRSTKY);
@ -545,5 +570,10 @@ pub fn sync(dacno: u8) -> Result<bool, &'static str> {
return Err("no sysref edge"); return Err("no sysref edge");
} }
let realign_occured = sync_status & ad9154_reg::SYNC_ROTATE != 0; let realign_occured = sync_status & ad9154_reg::SYNC_ROTATE != 0;
let phase_error = sync_status & ad9154_reg::SYNC_WLIM != 0;
if !realign_occured && phase_error {
// see also: SYNC_ERRWINDOW
warn!(" phase error window exceeded but clock did not rotate");
}
Ok(realign_occured) Ok(realign_occured)
} }

View File

@ -157,7 +157,7 @@ pub mod hmc7043 {
(false, FPGA_CLK_DIV, 0x10, true), // 5: AMC_FPGA_SYSREF1 (false, FPGA_CLK_DIV, 0x10, true), // 5: AMC_FPGA_SYSREF1
(false, 0, 0x10, false), // 6: unused (false, 0, 0x10, false), // 6: unused
(true, FPGA_CLK_DIV, 0x10, true), // 7: RTM_FPGA_SYSREF0 (true, FPGA_CLK_DIV, 0x10, true), // 7: RTM_FPGA_SYSREF0
(true, FPGA_CLK_DIV, 0x08, false), // 8: GTP_CLK0_IN (true, FPGA_CLK_DIV/2, 0x08, false), // 8: GTP_CLK0_IN
(false, 0, 0x10, false), // 9: unused (false, 0, 0x10, false), // 9: unused
(false, 0, 0x10, false), // 10: unused (false, 0, 0x10, false), // 10: unused
(false, 0, 0x08, false), // 11: unused / uFL (false, 0, 0x08, false), // 11: unused / uFL
@ -393,8 +393,6 @@ pub mod hmc7043 {
pub fn init() -> Result<(), &'static str> { pub fn init() -> Result<(), &'static str> {
#[cfg(all(hmc830_ref = "125", rtio_frequency = "125.0"))] #[cfg(all(hmc830_ref = "125", rtio_frequency = "125.0"))]
const DIV: (u32, u32, u32, u32) = (2, 32, 0, 1); // 125MHz -> 2.0GHz 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 */ /* do not use other SPI devices before HMC830 SPI mode selection */
hmc830::select_spi_mode(); hmc830::select_spi_mode();
@ -406,7 +404,7 @@ pub fn init() -> Result<(), &'static str> {
hmc830::check_locked()?; hmc830::check_locked()?;
if hmc7043::get_id() == hmc7043::CHIP_ID { if hmc7043::get_id() == hmc7043::CHIP_ID {
error!("HMC7043 detected while in reset (board rework missing?)"); error!("HMC7043 detected while in reset");
} }
hmc7043::enable(); hmc7043::enable();
hmc7043::detect()?; hmc7043::detect()?;

View File

@ -26,8 +26,6 @@ pub mod rpc_queue;
#[cfg(has_si5324)] #[cfg(has_si5324)]
pub mod si5324; pub mod si5324;
#[cfg(has_wrpll)]
pub mod wrpll;
#[cfg(has_hmc830_7043)] #[cfg(has_hmc830_7043)]
pub mod hmc830_7043; pub mod hmc830_7043;

View File

@ -1,538 +0,0 @@
use board_misoc::{csr, clock};
mod i2c {
use board_misoc::{csr, clock};
#[derive(Debug, Clone, Copy)]
pub enum Dcxo {
Main,
Helper
}
fn half_period() { clock::spin_us(1) }
const SDA_MASK: u8 = 2;
const SCL_MASK: u8 = 1;
fn sda_i(dcxo: Dcxo) -> bool {
let reg = match dcxo {
Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_in_read() },
Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_in_read() },
};
reg & SDA_MASK != 0
}
fn sda_oe(dcxo: Dcxo, oe: bool) {
let reg = match dcxo {
Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_read() },
Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_read() },
};
let reg = if oe { reg | SDA_MASK } else { reg & !SDA_MASK };
match dcxo {
Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_write(reg) },
Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_write(reg) }
}
}
fn sda_o(dcxo: Dcxo, o: bool) {
let reg = match dcxo {
Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_read() },
Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_read() },
};
let reg = if o { reg | SDA_MASK } else { reg & !SDA_MASK };
match dcxo {
Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_write(reg) },
Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_write(reg) }
}
}
fn scl_oe(dcxo: Dcxo, oe: bool) {
let reg = match dcxo {
Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_read() },
Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_read() },
};
let reg = if oe { reg | SCL_MASK } else { reg & !SCL_MASK };
match dcxo {
Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_write(reg) },
Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_write(reg) }
}
}
fn scl_o(dcxo: Dcxo, o: bool) {
let reg = match dcxo {
Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_read() },
Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_read() },
};
let reg = if o { reg | SCL_MASK } else { reg & !SCL_MASK };
match dcxo {
Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_write(reg) },
Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_write(reg) }
}
}
pub fn init(dcxo: Dcxo) -> Result<(), &'static str> {
// Set SCL as output, and high level
scl_o(dcxo, true);
scl_oe(dcxo, true);
// Prepare a zero level on SDA so that sda_oe pulls it down
sda_o(dcxo, false);
// Release SDA
sda_oe(dcxo, false);
// Check the I2C bus is ready
half_period();
half_period();
if !sda_i(dcxo) {
// Try toggling SCL a few times
for _bit in 0..8 {
scl_o(dcxo, false);
half_period();
scl_o(dcxo, true);
half_period();
}
}
if !sda_i(dcxo) {
return Err("SDA is stuck low and doesn't get unstuck");
}
Ok(())
}
pub fn start(dcxo: Dcxo) {
// Set SCL high then SDA low
scl_o(dcxo, true);
half_period();
sda_oe(dcxo, true);
half_period();
}
pub fn stop(dcxo: Dcxo) {
// First, make sure SCL is low, so that the target releases the SDA line
scl_o(dcxo, false);
half_period();
// Set SCL high then SDA high
sda_oe(dcxo, true);
scl_o(dcxo, true);
half_period();
sda_oe(dcxo, false);
half_period();
}
pub fn write(dcxo: Dcxo, data: u8) -> bool {
// MSB first
for bit in (0..8).rev() {
// Set SCL low and set our bit on SDA
scl_o(dcxo, false);
sda_oe(dcxo, data & (1 << bit) == 0);
half_period();
// Set SCL high ; data is shifted on the rising edge of SCL
scl_o(dcxo, true);
half_period();
}
// Check ack
// Set SCL low, then release SDA so that the I2C target can respond
scl_o(dcxo, false);
half_period();
sda_oe(dcxo, false);
// Set SCL high and check for ack
scl_o(dcxo, true);
half_period();
// returns true if acked (I2C target pulled SDA low)
!sda_i(dcxo)
}
pub fn read(dcxo: Dcxo, ack: bool) -> u8 {
// Set SCL low first, otherwise setting SDA as input may cause a transition
// on SDA with SCL high which will be interpreted as START/STOP condition.
scl_o(dcxo, false);
half_period(); // make sure SCL has settled low
sda_oe(dcxo, false);
let mut data: u8 = 0;
// MSB first
for bit in (0..8).rev() {
scl_o(dcxo, false);
half_period();
// Set SCL high and shift data
scl_o(dcxo, true);
half_period();
if sda_i(dcxo) { data |= 1 << bit }
}
// Send ack
// Set SCL low and pull SDA low when acking
scl_o(dcxo, false);
if ack { sda_oe(dcxo, true) }
half_period();
// then set SCL high
scl_o(dcxo, true);
half_period();
data
}
}
mod si549 {
use board_misoc::clock;
use super::i2c;
#[cfg(any(soc_platform = "metlino", soc_platform = "sayma_amc", soc_platform = "sayma_rtm"))]
pub const ADDRESS: u8 = 0x55;
#[cfg(soc_platform = "kasli")]
pub const ADDRESS: u8 = 0x67;
pub fn write(dcxo: i2c::Dcxo, reg: u8, val: u8) -> Result<(), &'static str> {
i2c::start(dcxo);
if !i2c::write(dcxo, ADDRESS << 1) {
return Err("Si549 failed to ack write address")
}
if !i2c::write(dcxo, reg) {
return Err("Si549 failed to ack register")
}
if !i2c::write(dcxo, val) {
return Err("Si549 failed to ack value")
}
i2c::stop(dcxo);
Ok(())
}
pub fn write_no_ack_value(dcxo: i2c::Dcxo, reg: u8, val: u8) -> Result<(), &'static str> {
i2c::start(dcxo);
if !i2c::write(dcxo, ADDRESS << 1) {
return Err("Si549 failed to ack write address")
}
if !i2c::write(dcxo, reg) {
return Err("Si549 failed to ack register")
}
i2c::write(dcxo, val);
i2c::stop(dcxo);
Ok(())
}
pub fn read(dcxo: i2c::Dcxo, reg: u8) -> Result<u8, &'static str> {
i2c::start(dcxo);
if !i2c::write(dcxo, ADDRESS << 1) {
return Err("Si549 failed to ack write address")
}
if !i2c::write(dcxo, reg) {
return Err("Si549 failed to ack register")
}
i2c::stop(dcxo);
i2c::start(dcxo);
if !i2c::write(dcxo, (ADDRESS << 1) | 1) {
return Err("Si549 failed to ack read address")
}
let val = i2c::read(dcxo, false);
i2c::stop(dcxo);
Ok(val)
}
pub fn program(dcxo: i2c::Dcxo, hsdiv: u16, lsdiv: u8, fbdiv: u64) -> Result<(), &'static str> {
i2c::init(dcxo)?;
write(dcxo, 255, 0x00)?; // PAGE
write_no_ack_value(dcxo, 7, 0x80)?; // RESET
clock::spin_us(100_000); // required? not specified in datasheet.
write(dcxo, 255, 0x00)?; // PAGE
write(dcxo, 69, 0x00)?; // Disable FCAL override.
// Note: Value 0x00 from Table 5.6 is inconsistent with Table 5.7,
// which shows bit 0 as reserved and =1.
write(dcxo, 17, 0x00)?; // Synchronously disable output
// The Si549 has no ID register, so we check that it responds correctly
// by writing values to a RAM-like register and reading them back.
for test_value in 0..255 {
write(dcxo, 23, test_value)?;
let readback = read(dcxo, 23)?;
if readback != test_value {
return Err("Si549 detection failed");
}
}
write(dcxo, 23, hsdiv as u8)?;
write(dcxo, 24, (hsdiv >> 8) as u8 | (lsdiv << 4))?;
write(dcxo, 26, fbdiv as u8)?;
write(dcxo, 27, (fbdiv >> 8) as u8)?;
write(dcxo, 28, (fbdiv >> 16) as u8)?;
write(dcxo, 29, (fbdiv >> 24) as u8)?;
write(dcxo, 30, (fbdiv >> 32) as u8)?;
write(dcxo, 31, (fbdiv >> 40) as u8)?;
write(dcxo, 7, 0x08)?; // Start FCAL
write(dcxo, 17, 0x01)?; // Synchronously enable output
Ok(())
}
// Si549 digital frequency trim ("all-digital PLL" register)
// ∆ f_out = adpll * 0.0001164e-6 (0.1164 ppb/lsb)
// max trim range is +- 950 ppm
pub fn set_adpll(dcxo: i2c::Dcxo, adpll: i32) -> Result<(), &'static str> {
write(dcxo, 231, adpll as u8)?;
write(dcxo, 232, (adpll >> 8) as u8)?;
write(dcxo, 233, (adpll >> 16) as u8)?;
clock::spin_us(100);
Ok(())
}
pub fn get_adpll(dcxo: i2c::Dcxo) -> Result<i32, &'static str> {
let b1 = read(dcxo, 231)? as i32;
let b2 = read(dcxo, 232)? as i32;
let b3 = read(dcxo, 233)? as i8 as i32;
Ok(b3 << 16 | b2 << 8 | b1)
}
}
// to do: load from gateware config
const DDMTD_COUNTER_N: u32 = 15;
const DDMTD_COUNTER_M: u32 = (1 << DDMTD_COUNTER_N);
const F_SYS: f64 = csr::CONFIG_CLOCK_FREQUENCY as f64;
const F_MAIN: f64 = 125.0e6;
const F_HELPER: f64 = F_MAIN * DDMTD_COUNTER_M as f64 / (DDMTD_COUNTER_M + 1) as f64;
const F_BEAT: f64 = F_MAIN - F_HELPER;
const TIME_STEP: f32 = 1./F_BEAT as f32;
fn ddmtd_tag_to_s(mu: f32) -> f32 {
return (mu as f32)*TIME_STEP;
}
fn get_frequencies() -> (u32, u32, u32) {
unsafe {
csr::wrpll::frequency_counter_update_en_write(1);
// wait for at least one full update cycle (> 2 timer periods)
clock::spin_us(200_000);
csr::wrpll::frequency_counter_update_en_write(0);
let helper = csr::wrpll::frequency_counter_counter_helper_read();
let main = csr::wrpll::frequency_counter_counter_rtio_read();
let cdr = csr::wrpll::frequency_counter_counter_rtio_rx0_read();
(helper, main, cdr)
}
}
fn log_frequencies() -> (u32, u32, u32) {
let (f_helper, f_main, f_cdr) = get_frequencies();
let conv_khz = |f| 4*(f as u64)*(csr::CONFIG_CLOCK_FREQUENCY as u64)/(1000*(1 << 23));
info!("helper clock frequency: {}kHz ({})", conv_khz(f_helper), f_helper);
info!("main clock frequency: {}kHz ({})", conv_khz(f_main), f_main);
info!("CDR clock frequency: {}kHz ({})", conv_khz(f_cdr), f_cdr);
(f_helper, f_main, f_cdr)
}
fn get_tags() -> (i32, i32, u16, u16) {
unsafe {
csr::wrpll::tag_arm_write(1);
while csr::wrpll::tag_arm_read() != 0 {}
let main_diff = csr::wrpll::main_diff_tag_read() as i32;
let helper_diff = csr::wrpll::helper_diff_tag_read() as i32;
let ref_tag = csr::wrpll::ref_tag_read();
let main_tag = csr::wrpll::main_tag_read();
(main_diff, helper_diff, ref_tag, main_tag)
}
}
fn print_tags() {
const NUM_TAGS: usize = 30;
let mut main_diffs = [0; NUM_TAGS]; // input to main loop filter
let mut helper_diffs = [0; NUM_TAGS]; // input to helper loop filter
let mut ref_tags = [0; NUM_TAGS];
let mut main_tags = [0; NUM_TAGS];
let mut jitter = [0 as f32; NUM_TAGS];
for i in 0..NUM_TAGS {
let (main_diff, helper_diff, ref_tag, main_tag) = get_tags();
main_diffs[i] = main_diff;
helper_diffs[i] = helper_diff;
ref_tags[i] = ref_tag;
main_tags[i] = main_tag;
}
info!("DDMTD ref tags: {:?}", ref_tags);
info!("DDMTD main tags: {:?}", main_tags);
info!("DDMTD main diffs: {:?}", main_diffs);
info!("DDMTD helper diffs: {:?}", helper_diffs);
// look at the difference between the main DCXO and reference...
let t0 = main_diffs[0];
main_diffs.iter_mut().for_each(|x| *x -= t0);
// crude estimate of the max difference across our sample set (assumes no unwrapping issues...)
let delta = main_diffs[main_diffs.len()-1] as f32 / (main_diffs.len()-1) as f32;
info!("detla: {:?} tags", delta);
let delta_f: f32 = delta/DDMTD_COUNTER_M as f32 * F_BEAT as f32;
info!("MAIN <-> ref frequency difference: {:?} Hz ({:?} ppm)", delta_f, delta_f/F_HELPER as f32 * 1e6);
jitter.iter_mut().enumerate().for_each(|(i, x)| *x = main_diffs[i] as f32 - delta*(i as f32));
info!("jitter: {:?} tags", jitter);
let var = jitter.iter().map(|x| x*x).fold(0 as f32, |acc, x| acc + x as f32) / NUM_TAGS as f32;
info!("variance: {:?} tags^2", var);
}
pub fn init() {
info!("initializing WR PLL...");
unsafe { csr::wrpll::helper_reset_write(1); }
unsafe {
csr::wrpll::helper_dcxo_i2c_address_write(si549::ADDRESS);
csr::wrpll::main_dcxo_i2c_address_write(si549::ADDRESS);
}
#[cfg(rtio_frequency = "125.0")]
let (h_hsdiv, h_lsdiv, h_fbdiv) = (0x05c, 0, 0x04b5badb98a);
#[cfg(rtio_frequency = "125.0")]
let (m_hsdiv, m_lsdiv, m_fbdiv) = (0x05c, 0, 0x04b5c447213);
si549::program(i2c::Dcxo::Main, m_hsdiv, m_lsdiv, m_fbdiv)
.expect("cannot initialize main Si549");
si549::program(i2c::Dcxo::Helper, h_hsdiv, h_lsdiv, h_fbdiv)
.expect("cannot initialize helper Si549");
// Si549 Settling Time for Large Frequency Change.
// Datasheet said 10ms but it lied.
clock::spin_us(50_000);
unsafe { csr::wrpll::helper_reset_write(0); }
clock::spin_us(1);
}
pub fn diagnostics() {
info!("WRPLL diagnostics...");
info!("Untrimmed oscillator frequencies:");
log_frequencies();
info!("Increase helper DCXO frequency by +10ppm (1.25kHz):");
si549::set_adpll(i2c::Dcxo::Helper, 85911).expect("ADPLL write failed");
// to do: add check on frequency?
log_frequencies();
}
fn trim_dcxos(f_helper: u32, f_main: u32, f_cdr: u32) -> Result<(i32, i32), &'static str> {
info!("Trimming oscillator frequencies...");
const DCXO_STEP: i64 = (1.0e6/0.0001164) as i64;
const ADPLL_MAX: i64 = (950.0/0.0001164) as i64;
const TIMER_WIDTH: u32 = 23;
const COUNTER_DIV: u32 = 2;
// how many counts we expect to measure
const SYS_COUNTS: i64 = (1 << (TIMER_WIDTH - COUNTER_DIV)) as i64;
const EXP_MAIN_COUNTS: i64 = ((SYS_COUNTS as f64) * (F_MAIN/F_SYS)) as i64;
const EXP_HELPER_COUNTS: i64 = ((SYS_COUNTS as f64) * (F_HELPER/F_SYS)) as i64;
// calibrate the SYS clock to the CDR clock and correct the measured counts
// assume frequency errors are small so we can make an additive correction
// positive error means sys clock is too fast
let sys_err: i64 = EXP_MAIN_COUNTS - (f_cdr as i64);
let main_err: i64 = EXP_MAIN_COUNTS - (f_main as i64) - sys_err;
let helper_err: i64 = EXP_HELPER_COUNTS - (f_helper as i64) - sys_err;
info!("sys count err {}", sys_err);
info!("main counts err {}", main_err);
info!("helper counts err {}", helper_err);
// calculate required adjustment to the ADPLL register see
// https://www.silabs.com/documents/public/data-sheets/si549-datasheet.pdf
// section 5.6
let helper_adpll: i64 = helper_err*DCXO_STEP/EXP_HELPER_COUNTS;
let main_adpll: i64 = main_err*DCXO_STEP/EXP_MAIN_COUNTS;
if helper_adpll.abs() > ADPLL_MAX {
return Err("helper DCXO offset too large");
}
if main_adpll.abs() > ADPLL_MAX {
return Err("main DCXO offset too large");
}
info!("ADPLL offsets: helper={} main={}", helper_adpll, main_adpll);
Ok((helper_adpll as i32, main_adpll as i32))
}
fn statistics(data: &[u16]) -> (f32, f32) {
let sum = data.iter().fold(0 as u32, |acc, x| acc + *x as u32);
let mean = sum as f32 / data.len() as f32;
let squared_sum = data.iter().fold(0 as u32, |acc, x| acc + (*x as u32).pow(2));
let variance = (squared_sum as f32 / data.len() as f32) - mean;
return (mean, variance)
}
fn select_recovered_clock_int(rc: bool) -> Result<(), &'static str> {
info!("Untrimmed oscillator frequencies:");
let (f_helper, f_main, f_cdr) = log_frequencies();
if rc {
let (helper_adpll, main_adpll) = trim_dcxos(f_helper, f_main, f_cdr)?;
// to do: add assertion on max frequency shift here?
si549::set_adpll(i2c::Dcxo::Helper, helper_adpll).expect("ADPLL write failed");
si549::set_adpll(i2c::Dcxo::Main, main_adpll).expect("ADPLL write failed");
log_frequencies();
clock::spin_us(100_000); // TO DO: remove/reduce!
print_tags();
info!("increasing main DCXO by 1ppm (125Hz):");
si549::set_adpll(i2c::Dcxo::Main, main_adpll + 8591).expect("ADPLL write failed");
clock::spin_us(100_000);
print_tags();
si549::set_adpll(i2c::Dcxo::Main, main_adpll).expect("ADPLL write failed");
unsafe {
csr::wrpll::adpll_offset_helper_write(helper_adpll as u32);
csr::wrpll::adpll_offset_main_write(main_adpll as u32);
csr::wrpll::helper_dcxo_gpio_enable_write(0);
csr::wrpll::main_dcxo_gpio_enable_write(0);
csr::wrpll::helper_dcxo_errors_write(0xff);
csr::wrpll::main_dcxo_errors_write(0xff);
csr::wrpll::collector_reset_write(0);
}
clock::spin_us(1_000); // wait for the collector to produce meaningful output
unsafe {
csr::wrpll::filter_reset_write(0);
}
clock::spin_us(100_000);
print_tags();
// let mut tags = [0; 10];
// for i in 0..tags.len() {
// tags[i] = get_ddmtd_helper_tag();
// }
// info!("DDMTD helper tags: {:?}", tags);
unsafe {
csr::wrpll::filter_reset_write(1);
csr::wrpll::collector_reset_write(1);
}
clock::spin_us(50_000);
unsafe {
csr::wrpll::helper_dcxo_gpio_enable_write(1);
csr::wrpll::main_dcxo_gpio_enable_write(1);
}
unsafe {
info!("error {} {}",
csr::wrpll::helper_dcxo_errors_read(),
csr::wrpll::main_dcxo_errors_read());
}
info!("new ADPLL: {} {}",
si549::get_adpll(i2c::Dcxo::Helper)?,
si549::get_adpll(i2c::Dcxo::Main)?);
} else {
si549::set_adpll(i2c::Dcxo::Helper, 0).expect("ADPLL write failed");
si549::set_adpll(i2c::Dcxo::Main, 0).expect("ADPLL write failed");
}
Ok(())
}
pub fn select_recovered_clock(rc: bool) {
if rc {
info!("switching to recovered clock");
} else {
info!("switching to local XO clock");
}
match select_recovered_clock_int(rc) {
Ok(()) => info!("clock transition completed"),
Err(e) => error!("clock transition failed: {}", e)
}
}

View File

@ -43,3 +43,5 @@ pub mod io_expander;
pub mod net_settings; pub mod net_settings;
#[cfg(has_slave_fpga_cfg)] #[cfg(has_slave_fpga_cfg)]
pub mod slave_fpga; pub mod slave_fpga;
#[cfg(has_mmcspi)]
pub mod mmcspi;

View File

@ -0,0 +1,200 @@
use super::{csr, clock};
// Sayma MMC SSP1 configuration:
//
// References:
// (i) Sayma MMC FPGA SPI port initialisation: https://github.com/sinara-hw/openMMC/blob/sayma-devel/modules/fpga_spi.c
// (ii) Sayma MMC configuration: https://github.com/sinara-hw/openMMC/blob/sayma-devel/port/ucontroller/nxp/lpc17xx/lpc17_ssp.c::ssp_init()
// (iii) openMMC SSP driver: https://github.com/sinara-hw/openMMC/blob/sayma-devel/port/ucontroller/nxp/lpc17xx/lpcopen/src/ssp_17xx_40xx.c)
//
// * Data Size Select <DSS>: 8-bit transfer (see FPGA_SPI_FRAME_SIZE)
// * Frame Format <FRF>: SPI (see lpc17_ssp.c::ssp_init())
// * Clock Out Polarity <CPOL>: CPOL=0 (CLK is low when idling) (see lpc17_ssp.c::ssp_init())
// * Clock Out Phase <CPHA>: CPHA=0 (data is captured on leading edge) (see lpc17_ssp.c::ssp_init())
// * CPOL=0, CPHA=0 ==> data is captured at rising edge
// * Clock Frequency: 10000000 == 10 MHz (see FPGA_SPI_BITRATE)
// TODO: consider making a generic SPI receiver for customisable configuration
static mut PREV_CS_N: bool = true; // High when idling
static mut PREV_CLK: bool = false; // Low when idling
// List of expected values
// openMMC modules/fpga_spi.h
const WR_COMMAND: u8 = 0x80;
const ADDR_HEADER: u16 = 0x0005; // "Data Valid Byte"
const DATA_HEADER: u32 = 0x55555555;
const FPGA_UPDATE_RATE: u64 = 5000; // Delay interval between broadcast
// Layout of MMC-to-FPGA data
// (see openMMC modules/fpga_spi.h board_diagnostic_t)
// cardID: u32 array of length 4
const ADDR_CARD_ID_0: u16 = 0; // cardID[0]: bits[31:24] = EUI48 byte 3
// bits[23:16] = EUI48 byte 2 (0x3D)
// bits[15: 8] = EUI48 byte 1 (0xC2)
// bits[ 7: 0] = EUI48 byte 0 (0xFC)
const ADDR_CARD_ID_1: u16 = 1; // cardID[1]: bits[47:40] = EUI48 byte 5
// bits[39:32] = EUI48 byte 4
const ADDR_SLOT_ID: u16 = 16; // Note: currently unused by FPGA
const ADDR_IPMI_ADDR: u16 = 20; // Note: currently unused by FPGA
const ADDR_DATA_VALID: u16 = 24; // Note: currently unused by FPGA
const ADDR_SENSOR: u16 = 28; // u32 array of length 21; see openMMC modules/sdr.h NUM_SENSOR
// Note: currently unused by FPGA
const ADDR_FMC_SLOT: u16 = 112; // Note: currently unused by FPGA
fn cs_n() -> bool {
unsafe { csr::mmcspi::cs_n_in_read() == 1 }
}
fn detect_cs_n_rise(timeout_us: u64) -> bool {
let start = clock::get_us();
while clock::get_us() - start < timeout_us {
if cs_n() && unsafe { !PREV_CS_N } {
unsafe { PREV_CS_N = true; }
return true;
}
}
false
}
fn detect_cs_n_fall(timeout_us: u64) -> bool {
let start = clock::get_us();
while clock::get_us() - start < timeout_us {
if !cs_n() && unsafe { PREV_CS_N } {
unsafe { PREV_CS_N = false; }
return true;
}
}
false
}
fn clk() -> bool {
unsafe { csr::mmcspi::clk_in_read() == 1 }
}
fn detect_clk_rise(timeout_us: u64) -> bool {
let start = clock::get_us();
while clock::get_us() - start < timeout_us {
if clk() && unsafe { !PREV_CLK } {
unsafe { PREV_CLK = true; }
return true;
}
}
false
}
fn detect_clk_fall(timeout_us: u64) -> bool {
let start = clock::get_us();
while clock::get_us() - start < timeout_us {
if !clk() && unsafe { PREV_CLK } {
unsafe { PREV_CLK = false; }
return true;
}
}
false
}
fn mosi() -> u8 {
unsafe { csr::mmcspi::mosi_in_read() & 1 }
}
/// Detects CS_n assertion and keeps reading until the buffer is full or CS_n is deasserted
/// TODO: Generalise this driver for future possible changes to the MMC SPI master settings
fn read_continuous(buf: &mut [u8], timeout_ms: u64) {
// Register CS_n and CLK states
unsafe {
// Give up if CS_n has already been asserted (we're in the middle of transaction)
if !cs_n() { return } else { PREV_CS_N = true }
PREV_CLK = clk();
}
// Wait until timeout or CS_n falling edge is detected, which indicates a new transaction
if !detect_cs_n_fall(timeout_ms * 1000) { return }
for byte_ind in 0..buf.len() {
// Read bits from MSB to LSB
for bit_ind in (0..8).rev() {
// If CS_n goes high, return to indicate a complete SPI transaction
if cs_n() { break }
// Detect and register CLK rising edge
if !detect_clk_rise(1000) { return }
// Store the MOSI state as the current bit of the current byte
if mosi() == 1 {
buf[byte_ind] |= 1 << bit_ind;
}
// Detect and register CLK falling edge
if !detect_clk_fall(1000) { return }
}
}
}
/// Convert bytes to u16 (from big-endian)
fn to_u16(buf: &[u8]) -> u16 {
let mut value = 0_u16;
for i in 0..2 {
value |= (buf[i] as u16) << ((1-i) * 8);
}
value
}
/// Convert bytes to u32 (from big-endian)
fn to_u32(buf: &[u8]) -> u32 {
let mut value = 0_u32;
for i in 0..4 {
value |= (buf[i] as u32) << ((3-i) * 8);
}
value
}
/// Check if the bytes are the MMC broadcast header
fn is_broadcast_header(buf: &[u8]) -> bool {
buf.len() == 7 &&
buf[0] == WR_COMMAND &&
to_u16(&buf[1..3]) == ADDR_HEADER &&
to_u32(&buf[3..7]) == DATA_HEADER
}
/// Read the SPI to wait and capture the EUI48, and store it to a u8 array;
/// Returns Ok() to indicate if the data is captured
pub fn read_eui48(buf: &mut [u8]) -> Result<(), ()> {
assert!(buf.len() >= 6);
let mut spi_buf = [0_u8; 21];
let mut is_broadcast = false;
// Loop 10s to read a continuous byte transaction until the header correspond to the MMC broadcast format
let start = clock::get_ms();
while !is_broadcast && clock::get_ms() - start <= 10_000 {
// Read 21 contiguous bytes in a row, which is broadcast every 5 seconds
read_continuous(&mut spi_buf, FPGA_UPDATE_RATE + 100); // +100ms margin
// Check the header
is_broadcast = is_broadcast_header(&spi_buf[0..7]);
}
// Return Err(()) if no broadcast header is detected
if !is_broadcast { return Err(()) }
// Truncate the header to get all data captured
let data = &spi_buf[7..];
let (mut eui48_lo_ok, mut eui48_hi_ok) = (false, false);
for i in 0..data.len()/7 {
match to_u16(&data[i*7+1..i*7+3]) {
// EUI48[31:0], big-endian
ADDR_CARD_ID_0 => {
for j in 0..4 { buf[j] = data[i*7 + 6-j] }
eui48_lo_ok = true;
}
// EUI48[47:32], big-endian
ADDR_CARD_ID_1 => {
for j in 0..2 { buf[4 + j] = data[i*7 + 6-j] }
eui48_hi_ok = true;
}
_ => {}
}
}
match (eui48_lo_ok, eui48_hi_ok) {
(true, true) => Ok(()),
// This should never return Err(), unless the broadcast format has changed
_ => Err(()),
}
}

View File

@ -5,6 +5,8 @@ use smoltcp::wire::{EthernetAddress, IpAddress};
use config; use config;
#[cfg(soc_platform = "kasli")] #[cfg(soc_platform = "kasli")]
use i2c_eeprom; use i2c_eeprom;
#[cfg(soc_platform = "sayma_amc")]
use mmcspi;
pub struct NetAddresses { pub struct NetAddresses {
@ -40,7 +42,11 @@ pub fn get_adresses() -> NetAddresses {
.unwrap_or_else(|_e| EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x21])); .unwrap_or_else(|_e| EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x21]));
} }
#[cfg(soc_platform = "sayma_amc")] #[cfg(soc_platform = "sayma_amc")]
{ hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x11]); } {
let mut eui48 = [0x02, 0x00, 0x00, 0x00, 0x00, 0x11];
let _ = mmcspi::read_eui48(&mut eui48);
hardware_addr = EthernetAddress(eui48);
}
#[cfg(soc_platform = "metlino")] #[cfg(soc_platform = "metlino")]
{ hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x19]); } { hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x19]); }
#[cfg(soc_platform = "kc705")] #[cfg(soc_platform = "kc705")]

View File

@ -1,5 +1,6 @@
#![no_std] #![no_std]
#![cfg_attr(feature = "alloc", feature(alloc))] #![cfg_attr(feature = "alloc", feature(alloc))]
#![feature(extern_prelude)]
extern crate failure; extern crate failure;
#[macro_use] #[macro_use]

View File

@ -36,8 +36,12 @@ unsafe fn recv_value<R, E>(reader: &mut R, tag: Tag, data: &mut *mut (),
Tag::String | Tag::Bytes | Tag::ByteArray => { Tag::String | Tag::Bytes | Tag::ByteArray => {
consume_value!(CMutSlice<u8>, |ptr| { consume_value!(CMutSlice<u8>, |ptr| {
let length = reader.read_u32()? as usize; let length = reader.read_u32()? as usize;
if length > 0 {
*ptr = CMutSlice::new(alloc(length)? as *mut u8, length); *ptr = CMutSlice::new(alloc(length)? as *mut u8, length);
reader.read_exact((*ptr).as_mut())?; reader.read_exact((*ptr).as_mut())?;
} else {
*ptr = CMutSlice::new(core::ptr::NonNull::<u8>::dangling().as_ptr(), 0);
}
Ok(()) Ok(())
}) })
} }

View File

@ -119,19 +119,6 @@ fn setup_si5324_as_synthesizer() {
bwsel : 4, bwsel : 4,
crystal_ref: true crystal_ref: true
}; };
// 150MHz output, from crystal
#[cfg(all(rtio_frequency = "150.0", not(si5324_ext_ref)))]
const SI5324_SETTINGS: si5324::FrequencySettings
= si5324::FrequencySettings {
n1_hs : 9,
nc1_ls : 4,
n2_hs : 10,
n2_ls : 33732,
n31 : 7139,
n32 : 7139,
bwsel : 3,
crystal_ref: true
};
// 100MHz output, from crystal. Also used as reference for Sayma HMC830. // 100MHz output, from crystal. Also used as reference for Sayma HMC830.
#[cfg(all(rtio_frequency = "100.0", not(si5324_ext_ref)))] #[cfg(all(rtio_frequency = "100.0", not(si5324_ext_ref)))]
const SI5324_SETTINGS: si5324::FrequencySettings const SI5324_SETTINGS: si5324::FrequencySettings

View File

@ -9,6 +9,7 @@ pub const SYNC: u8 = 0x12;
pub const DDMTD_SYSREF_RAW: u8 = 0x20; pub const DDMTD_SYSREF_RAW: u8 = 0x20;
pub const DDMTD_SYSREF: u8 = 0x21; pub const DDMTD_SYSREF: u8 = 0x21;
pub const DDMTD_INIT: u8 = 0x22;
fn average_2phases(a: i32, b: i32, modulo: i32) -> i32 { fn average_2phases(a: i32, b: i32, modulo: i32) -> i32 {

View File

@ -140,7 +140,7 @@ pub mod jdac {
pub mod jesd204sync { pub mod jesd204sync {
use board_misoc::{csr, clock, config}; use board_misoc::{csr, clock, config};
use super::jdac; use super::{jdac, jesd};
use super::super::jdac_common; use super::super::jdac_common;
const HMC7043_ANALOG_DELAY_RANGE: u8 = 24; const HMC7043_ANALOG_DELAY_RANGE: u8 = 24;
@ -410,9 +410,23 @@ pub mod jesd204sync {
} }
pub fn sysref_auto_rtio_align() -> Result<(), &'static str> { pub fn sysref_auto_rtio_align() -> Result<(), &'static str> {
for i in 0..3 { // Allow resetting DDMTD core 2 times at max
let result = {
test_ddmtd_stability(true, 4)?; test_ddmtd_stability(true, 4)?;
test_ddmtd_stability(false, 1)?; test_ddmtd_stability(false, 1)?;
test_slip_ddmtd()?; test_slip_ddmtd()
};
if let Err(_) = result {
if i == 3 {
error!("SYSREF test failed with too many retries");
return result
}
warn!("SYSREF test failed, retrying...");
jdac::basic_request(0, jdac_common::DDMTD_INIT, 0)?;
jesd::reset(false);
let _ = jdac::init();
} else { break }
}
info!("determining SYSREF S/H limits..."); info!("determining SYSREF S/H limits...");
let sysref_sh_limits = measure_sysref_sh_limits()?; let sysref_sh_limits = measure_sysref_sh_limits()?;

View File

@ -11,8 +11,6 @@ use core::convert::TryFrom;
use board_misoc::{csr, irq, ident, clock, uart_logger, i2c}; use board_misoc::{csr, irq, ident, clock, uart_logger, i2c};
#[cfg(has_si5324)] #[cfg(has_si5324)]
use board_artiq::si5324; use board_artiq::si5324;
#[cfg(has_wrpll)]
use board_artiq::wrpll;
use board_artiq::{spi, drtioaux}; use board_artiq::{spi, drtioaux};
use board_artiq::drtio_routing; use board_artiq::drtio_routing;
#[cfg(has_hmc830_7043)] #[cfg(has_hmc830_7043)]
@ -302,9 +300,7 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
#[cfg(has_ad9154)] #[cfg(has_ad9154)]
let (succeeded, retval) = { let (succeeded, retval) = {
#[cfg(rtio_frequency = "125.0")] #[cfg(rtio_frequency = "125.0")]
const LINERATE: u64 = 5_000_000_000; const LINERATE: u64 = 10_000_000_000;
#[cfg(rtio_frequency = "150.0")]
const LINERATE: u64 = 6_000_000_000;
match _reqno { match _reqno {
jdac_common::INIT => (board_artiq::ad9154::setup(_dacno, LINERATE).is_ok(), 0), jdac_common::INIT => (board_artiq::ad9154::setup(_dacno, LINERATE).is_ok(), 0),
jdac_common::PRINT_STATUS => { board_artiq::ad9154::status(_dacno); (true, 0) }, jdac_common::PRINT_STATUS => { board_artiq::ad9154::status(_dacno); (true, 0) },
@ -324,7 +320,8 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
}, },
jdac_common::DDMTD_SYSREF_RAW => (true, jdac_common::measure_ddmdt_phase_raw() as u8), jdac_common::DDMTD_SYSREF_RAW => (true, jdac_common::measure_ddmdt_phase_raw() as u8),
jdac_common::DDMTD_SYSREF => (true, jdac_common::measure_ddmdt_phase() as u8), jdac_common::DDMTD_SYSREF => (true, jdac_common::measure_ddmdt_phase() as u8),
_ => (false, 0) jdac_common::DDMTD_INIT => (init_ddmtd_reset_ad9154().is_ok(), 0),
_ => (false, 0),
} }
}; };
#[cfg(not(has_ad9154))] #[cfg(not(has_ad9154))]
@ -419,18 +416,14 @@ fn hardware_tick(ts: &mut u64) {
} }
} }
#[cfg(all(has_si5324, rtio_frequency = "150.0"))] #[cfg(has_ad9154)]
const SI5324_SETTINGS: si5324::FrequencySettings fn init_ddmtd_reset_ad9154() -> Result<(), &'static str> {
= si5324::FrequencySettings { jdac_common::init_ddmtd()?;
n1_hs : 6, for dacno in 0..csr::CONFIG_AD9154_COUNT {
nc1_ls : 6, board_artiq::ad9154::reset_and_detect(dacno as u8)?;
n2_hs : 10, }
n2_ls : 270, Ok(())
n31 : 75, }
n32 : 75,
bwsel : 4,
crystal_ref: true
};
#[cfg(all(has_si5324, rtio_frequency = "125.0"))] #[cfg(all(has_si5324, rtio_frequency = "125.0"))]
const SI5324_SETTINGS: si5324::FrequencySettings const SI5324_SETTINGS: si5324::FrequencySettings
@ -464,17 +457,6 @@ pub extern fn main() -> i32 {
io_expander1 = board_misoc::io_expander::IoExpander::new(1); io_expander1 = board_misoc::io_expander::IoExpander::new(1);
io_expander0.init().expect("I2C I/O expander #0 initialization failed"); io_expander0.init().expect("I2C I/O expander #0 initialization failed");
io_expander1.init().expect("I2C I/O expander #1 initialization failed"); io_expander1.init().expect("I2C I/O expander #1 initialization failed");
#[cfg(has_wrpll)]
{
io_expander0.set_oe(1, 1 << 7).unwrap();
io_expander0.set(1, 7, true);
io_expander0.service().unwrap();
io_expander1.set_oe(0, 1 << 7).unwrap();
io_expander1.set_oe(1, 1 << 7).unwrap();
io_expander1.set(0, 7, true);
io_expander1.set(1, 7, true);
io_expander1.service().unwrap();
}
// Actively drive TX_DISABLE to false on SFP0..3 // Actively drive TX_DISABLE to false on SFP0..3
io_expander0.set_oe(0, 1 << 1).unwrap(); io_expander0.set_oe(0, 1 << 1).unwrap();
@ -491,8 +473,6 @@ pub extern fn main() -> i32 {
#[cfg(has_si5324)] #[cfg(has_si5324)]
si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324"); si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324");
#[cfg(has_wrpll)]
wrpll::init();
unsafe { unsafe {
csr::drtio_transceiver::stable_clkin_write(1); csr::drtio_transceiver::stable_clkin_write(1);
@ -502,8 +482,6 @@ pub extern fn main() -> i32 {
unsafe { unsafe {
csr::drtio_transceiver::txenable_write(0xffffffffu32 as _); csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
} }
#[cfg(has_wrpll)]
wrpll::diagnostics();
init_rtio_crg(); init_rtio_crg();
#[cfg(has_hmc830_7043)] #[cfg(has_hmc830_7043)]
@ -554,8 +532,6 @@ pub extern fn main() -> i32 {
si5324::siphaser::select_recovered_clock(true).expect("failed to switch clocks"); si5324::siphaser::select_recovered_clock(true).expect("failed to switch clocks");
si5324::siphaser::calibrate_skew().expect("failed to calibrate skew"); si5324::siphaser::calibrate_skew().expect("failed to calibrate skew");
} }
#[cfg(has_wrpll)]
wrpll::select_recovered_clock(true);
drtioaux::reset(0); drtioaux::reset(0);
drtiosat_reset(false); drtiosat_reset(false);
@ -604,7 +580,7 @@ pub extern fn main() -> i32 {
if is_up && !was_up { if is_up && !was_up {
/* /*
* One side of the JESD204 elastic buffer is clocked by the jitter filter * One side of the JESD204 elastic buffer is clocked by the jitter filter
* (Si5324 or WRPLL), the other by the RTM. * (Si5324), the other by the RTM.
* The elastic buffer can operate only when those two clocks are derived from * The elastic buffer can operate only when those two clocks are derived from
* the same oscillator. * the same oscillator.
* This is the case when either of those conditions is true: * This is the case when either of those conditions is true:
@ -636,8 +612,6 @@ pub extern fn main() -> i32 {
info!("uplink is down, switching to local oscillator clock"); info!("uplink is down, switching to local oscillator clock");
#[cfg(has_si5324)] #[cfg(has_si5324)]
si5324::siphaser::select_recovered_clock(false).expect("failed to switch clocks"); si5324::siphaser::select_recovered_clock(false).expect("failed to switch clocks");
#[cfg(has_wrpll)]
wrpll::select_recovered_clock(false);
} }
} }

View File

@ -39,11 +39,18 @@ async def get_logs_sim(host):
async def get_logs(host): async def get_logs(host):
reader, writer = await asyncio.open_connection(host, 1380) reader, writer = await asyncio.open_connection(host, 1380)
writer.write(b"ARTIQ management\n") writer.write(b"ARTIQ management\n")
endian = await reader.readexactly(1)
if endian == b"e":
endian = "<"
elif endian == b"E":
endian = ">"
else:
raise IOError("Incorrect reply from device: expected e/E.")
writer.write(struct.pack("B", Request.PullLog.value)) writer.write(struct.pack("B", Request.PullLog.value))
await writer.drain() await writer.drain()
while True: while True:
length, = struct.unpack(">l", await reader.readexactly(4)) length, = struct.unpack(endian + "l", await reader.readexactly(4))
log = await reader.readexactly(length) log = await reader.readexactly(length)
for line in log.decode("utf-8").splitlines(): for line in log.decode("utf-8").splitlines():

View File

@ -11,9 +11,14 @@ from artiq.coredevice import jsondesc
def process_header(output, description): def process_header(output, description):
if description["target"] != "kasli": if description["target"] not in ("kasli", "kasli_soc"):
raise NotImplementedError raise NotImplementedError
cpu_target = {
"kasli": "or1k",
"kasli_soc": "cortexa9"
}[description["target"]]
print(textwrap.dedent(""" print(textwrap.dedent("""
# Autogenerated for the {variant} variant # Autogenerated for the {variant} variant
core_addr = "{core_addr}" core_addr = "{core_addr}"
@ -23,7 +28,7 @@ def process_header(output, description):
"type": "local", "type": "local",
"module": "artiq.coredevice.core", "module": "artiq.coredevice.core",
"class": "Core", "class": "Core",
"arguments": {{"host": core_addr, "ref_period": {ref_period}}} "arguments": {{"host": core_addr, "ref_period": {ref_period}, "target": "{cpu_target}"}},
}}, }},
"core_log": {{ "core_log": {{
"type": "controller", "type": "controller",
@ -58,7 +63,8 @@ def process_header(output, description):
""").format( """).format(
variant=description["variant"], variant=description["variant"],
core_addr=description["core_addr"], core_addr=description["core_addr"],
ref_period=1/(8*description["rtio_frequency"])), ref_period=1/(8*description["rtio_frequency"]),
cpu_target=cpu_target),
file=output) file=output)
@ -268,6 +274,9 @@ class PeripheralManager:
name=mirny_name, name=mirny_name,
mchn=i) mchn=i)
clk_sel = peripheral["clk_sel"]
if isinstance(peripheral["clk_sel"], str):
clk_sel = '"' + peripheral["clk_sel"] + '"'
self.gen(""" self.gen("""
device_db["{name}_cpld"] = {{ device_db["{name}_cpld"] = {{
"type": "local", "type": "local",
@ -281,7 +290,7 @@ class PeripheralManager:
}}""", }}""",
name=mirny_name, name=mirny_name,
refclk=peripheral["refclk"], refclk=peripheral["refclk"],
clk_sel=peripheral["clk_sel"]) clk_sel=clk_sel)
return next(channel) return next(channel)
@ -510,7 +519,7 @@ class PeripheralManager:
processor = getattr(self, "process_"+str(peripheral["type"])) processor = getattr(self, "process_"+str(peripheral["type"]))
return processor(rtio_offset, peripheral) return processor(rtio_offset, peripheral)
def add_sfp_leds(self, rtio_offset): def add_board_leds(self, rtio_offset):
for i in range(2): for i in range(2):
self.gen(""" self.gen("""
device_db["{name}"] = {{ device_db["{name}"] = {{
@ -541,8 +550,8 @@ def process(output, master_description, satellites):
for peripheral in master_description["peripherals"]: for peripheral in master_description["peripherals"]:
n_channels = pm.process(rtio_offset, peripheral) n_channels = pm.process(rtio_offset, peripheral)
rtio_offset += n_channels rtio_offset += n_channels
if base == "standalone" and master_description["hw_rev"] in ("v1.0", "v1.1"): if base == "standalone":
n_channels = pm.add_sfp_leds(rtio_offset) n_channels = pm.add_board_leds(rtio_offset)
rtio_offset += n_channels rtio_offset += n_channels
for destination, description in satellites: for destination, description in satellites:

View File

@ -79,23 +79,22 @@ Prerequisites:
help="actions to perform, default: flash everything") help="actions to perform, default: flash everything")
return parser return parser
def openocd_root():
openocd = shutil.which("openocd")
if not openocd:
raise FileNotFoundError("OpenOCD is required but was not found in PATH. Is it installed?")
return os.path.dirname(os.path.dirname(openocd))
def scripts_path(): def scripts_path():
p = ["share", "openocd", "scripts"] p = ["share", "openocd", "scripts"]
if os.name == "nt": if os.name == "nt":
p.insert(0, "Library") p.insert(0, "Library")
p = os.path.abspath(os.path.join( return os.path.abspath(os.path.join(openocd_root(), *p))
os.path.dirname(os.path.realpath(shutil.which("openocd"))),
"..", *p))
return p
def proxy_path(): def proxy_path():
p = ["share", "bscan-spi-bitstreams"] return os.path.abspath(os.path.join(openocd_root(), "share", "bscan-spi-bitstreams"))
p = os.path.abspath(os.path.join(
os.path.dirname(os.path.realpath(shutil.which("openocd"))),
"..", *p))
return p
def find_proxy_bitfile(filename): def find_proxy_bitfile(filename):
@ -441,7 +440,7 @@ def main():
storage_img = args.storage storage_img = args.storage
programmer.write_binary(*config["storage"], storage_img) programmer.write_binary(*config["storage"], storage_img)
elif action == "firmware": elif action == "firmware":
if variant.endswith("satellite"): if "satellite" in variant:
firmware = "satman" firmware = "satman"
else: else:
firmware = "runtime" firmware = "runtime"

View File

@ -21,7 +21,6 @@ from artiq.master.databases import DeviceDB, DatasetDB
from artiq.master.worker_db import DeviceManager, DatasetManager from artiq.master.worker_db import DeviceManager, DatasetManager
from artiq.coredevice.core import CompileError, host_only from artiq.coredevice.core import CompileError, host_only
from artiq.compiler.embedding import EmbeddingMap from artiq.compiler.embedding import EmbeddingMap
from artiq.compiler.targets import OR1KTarget
from artiq.compiler import import_cache from artiq.compiler import import_cache
from artiq.tools import * from artiq.tools import *
@ -53,7 +52,7 @@ class FileRunner(EnvExperiment):
def build(self, file): def build(self, file):
self.setattr_device("core") self.setattr_device("core")
self.file = file self.file = file
self.target = OR1KTarget() self.target = self.core.target_cls()
def run(self): def run(self):
kernel_library = self.compile() kernel_library = self.compile()

View File

@ -101,7 +101,8 @@ class SinaraTester(EnvExperiment):
sw_device = desc["arguments"]["sw_device"] sw_device = desc["arguments"]["sw_device"]
del self.ttl_outs[sw_device] del self.ttl_outs[sw_device]
elif (module, cls) == ("artiq.coredevice.urukul", "CPLD"): elif (module, cls) == ("artiq.coredevice.urukul", "CPLD"):
io_update_device = desc["arguments"]["io_update_device"] io_update_device = desc["arguments"].get("io_update_device", None)
if io_update_device is not None:
del self.ttl_outs[io_update_device] del self.ttl_outs[io_update_device]
elif (module, cls) == ("artiq.coredevice.sampler", "Sampler"): elif (module, cls) == ("artiq.coredevice.sampler", "Sampler"):
cnv_device = desc["arguments"]["cnv_device"] cnv_device = desc["arguments"]["cnv_device"]
@ -296,6 +297,7 @@ class SinaraTester(EnvExperiment):
@kernel @kernel
def setup_mirny(self, channel, frequency): def setup_mirny(self, channel, frequency):
self.core.break_realtime() self.core.break_realtime()
delay(1*ms)
channel.init() channel.init()
channel.set_att_mu(160) channel.set_att_mu(160)

View File

@ -4,20 +4,20 @@ from migen.genlib.cdc import MultiReg, PulseSynchronizer
from misoc.interconnect.csr import * from misoc.interconnect.csr import *
# This code assumes 125/62.5MHz reference clock and 125MHz or 150MHz RTIO # This code assumes 125/62.5MHz reference clock and 125MHz RTIO
# frequency. # frequency.
class SiPhaser7Series(Module, AutoCSR): class SiPhaser7Series(Module, AutoCSR):
def __init__(self, si5324_clkin, rx_synchronizer, def __init__(self, si5324_clkin, rx_synchronizer,
ref_clk=None, ref_div2=False, ultrascale=False, rtio_clk_freq=150e6): ref_clk=None, ref_div2=False, ultrascale=False, rtio_clk_freq=125e6):
self.switch_clocks = CSRStorage() self.switch_clocks = CSRStorage()
self.phase_shift = CSR() self.phase_shift = CSR()
self.phase_shift_done = CSRStatus(reset=1) self.phase_shift_done = CSRStatus(reset=1)
self.error = CSR() self.error = CSR()
assert rtio_clk_freq in (125e6, 150e6) assert rtio_clk_freq == 125e6
# 125MHz/62.5MHz reference clock to 125MHz/150MHz. VCO @ 750MHz. # 125MHz reference clock to 125MHz. VCO @ 750MHz.
# Used to provide a startup clock to the transceiver through the Si, # Used to provide a startup clock to the transceiver through the Si,
# we do not use the crystal reference so that the PFD (f3) frequency # we do not use the crystal reference so that the PFD (f3) frequency
# can be high. # can be high.
@ -43,8 +43,8 @@ class SiPhaser7Series(Module, AutoCSR):
else: else:
mmcm_freerun_output = mmcm_freerun_output_raw mmcm_freerun_output = mmcm_freerun_output_raw
# 125MHz/150MHz to 125MHz/150MHz with controllable phase shift, # 125MHz to 125MHz with controllable phase shift,
# VCO @ 1000MHz/1200MHz. # VCO @ 1000MHz.
# Inserted between CDR and output to Si, used to correct # Inserted between CDR and output to Si, used to correct
# non-determinstic skew of Si5324. # non-determinstic skew of Si5324.
mmcm_ps_fb = Signal() mmcm_ps_fb = Signal()

View File

@ -77,8 +77,6 @@ class GTHSingle(Module):
p_ALIGN_PCOMMA_DET ="FALSE", p_ALIGN_PCOMMA_DET ="FALSE",
p_ALIGN_PCOMMA_VALUE =0b0101111100, p_ALIGN_PCOMMA_VALUE =0b0101111100,
p_A_RXOSCALRESET =0b0, p_A_RXOSCALRESET =0b0,
p_A_RXPROGDIVRESET =0b0,
p_A_TXPROGDIVRESET =0b0,
p_CBCC_DATA_SOURCE_SEL ="ENCODED", p_CBCC_DATA_SOURCE_SEL ="ENCODED",
p_CDR_SWAP_MODE_EN =0b0, p_CDR_SWAP_MODE_EN =0b0,
p_CHAN_BOND_KEEP_ALIGN ="FALSE", p_CHAN_BOND_KEEP_ALIGN ="FALSE",
@ -314,7 +312,7 @@ class GTHSingle(Module):
p_RX_BIAS_CFG0 =0b0000101010110100, p_RX_BIAS_CFG0 =0b0000101010110100,
p_RX_BUFFER_CFG =0b000000, p_RX_BUFFER_CFG =0b000000,
p_RX_CAPFF_SARC_ENB =0b0, p_RX_CAPFF_SARC_ENB =0b0,
p_RX_CLK25_DIV =6, p_RX_CLK25_DIV =5, # Applicable to 125MHz RXPLLREFCLK_DIV1 = CPLL GTREFCLK0
p_RX_CLKMUX_EN =0b1, p_RX_CLKMUX_EN =0b1,
p_RX_CLK_SLIP_OVRD =0b00000, p_RX_CLK_SLIP_OVRD =0b00000,
p_RX_CM_BUF_CFG =0b1010, p_RX_CM_BUF_CFG =0b1010,
@ -413,7 +411,7 @@ class GTHSingle(Module):
p_TXSYNC_MULTILANE =0 if mode == "single" else 1, p_TXSYNC_MULTILANE =0 if mode == "single" else 1,
p_TXSYNC_OVRD =0b0, p_TXSYNC_OVRD =0b0,
p_TXSYNC_SKIP_DA =0b0, p_TXSYNC_SKIP_DA =0b0,
p_TX_CLK25_DIV =6, p_TX_CLK25_DIV =5, # Applicable to 125MHz TXPLLREFCLK_DIV1 = CPLL GTREFCLK0
p_TX_CLKMUX_EN =0b1, p_TX_CLKMUX_EN =0b1,
p_TX_DATA_WIDTH =dw, p_TX_DATA_WIDTH =dw,
p_TX_DCD_CFG =0b000010, p_TX_DCD_CFG =0b000010,
@ -475,6 +473,7 @@ class GTHSingle(Module):
# TX Startup/Reset # TX Startup/Reset
i_GTTXRESET=tx_init.gtXxreset, i_GTTXRESET=tx_init.gtXxreset,
i_TXPROGDIVRESET=tx_init.gtXxprogdivreset,
o_TXRESETDONE=tx_init.Xxresetdone, o_TXRESETDONE=tx_init.Xxresetdone,
i_TXDLYSRESET=tx_init.Xxdlysreset if mode != "slave" else self.txdlysreset, i_TXDLYSRESET=tx_init.Xxdlysreset if mode != "slave" else self.txdlysreset,
o_TXDLYSRESETDONE=tx_init.Xxdlysresetdone, o_TXDLYSRESETDONE=tx_init.Xxdlysresetdone,
@ -501,6 +500,7 @@ class GTHSingle(Module):
# RX Startup/Reset # RX Startup/Reset
i_GTRXRESET=rx_init.gtXxreset, i_GTRXRESET=rx_init.gtXxreset,
i_RXPROGDIVRESET=rx_init.gtXxprogdivreset,
o_RXRESETDONE=rx_init.Xxresetdone, o_RXRESETDONE=rx_init.Xxresetdone,
i_RXDLYSRESET=rx_init.Xxdlysreset, i_RXDLYSRESET=rx_init.Xxdlysreset,
o_RXPHALIGNDONE=rxphaligndone, o_RXPHALIGNDONE=rxphaligndone,

View File

@ -18,6 +18,8 @@ class GTHInit(Module):
self.plllock = Signal() self.plllock = Signal()
self.pllreset = Signal() self.pllreset = Signal()
self.gtXxreset = Signal() self.gtXxreset = Signal()
# Reset signal for programmable divider: https://www.xilinx.com/support/answers/64103.html
self.gtXxprogdivreset = Signal()
self.Xxresetdone = Signal() self.Xxresetdone = Signal()
self.Xxdlysreset = Signal() self.Xxdlysreset = Signal()
self.Xxdlysresetdone = Signal() self.Xxdlysresetdone = Signal()
@ -46,10 +48,12 @@ class GTHInit(Module):
# Deglitch FSM outputs driving transceiver asynch inputs # Deglitch FSM outputs driving transceiver asynch inputs
gtXxreset = Signal() gtXxreset = Signal()
gtXxprogdivreset = Signal()
Xxdlysreset = Signal() Xxdlysreset = Signal()
Xxuserrdy = Signal() Xxuserrdy = Signal()
self.sync += [ self.sync += [
self.gtXxreset.eq(gtXxreset), self.gtXxreset.eq(gtXxreset),
self.gtXxprogdivreset.eq(gtXxprogdivreset),
self.Xxdlysreset.eq(Xxdlysreset), self.Xxdlysreset.eq(Xxdlysreset),
self.Xxuserrdy.eq(Xxuserrdy) self.Xxuserrdy.eq(Xxuserrdy)
] ]
@ -80,6 +84,7 @@ class GTHInit(Module):
startup_fsm.act("RESET_ALL", startup_fsm.act("RESET_ALL",
gtXxreset.eq(1), gtXxreset.eq(1),
gtXxprogdivreset.eq(1),
self.pllreset.eq(1), self.pllreset.eq(1),
pll_reset_timer.wait.eq(1), pll_reset_timer.wait.eq(1),
If(pll_reset_timer.done, If(pll_reset_timer.done,

View File

@ -1,2 +0,0 @@
from artiq.gateware.drtio.wrpll.core import WRPLL
from artiq.gateware.drtio.wrpll.ddmtd import DDMTDSamplerExtFF, DDMTDSamplerGTP

View File

@ -1,156 +0,0 @@
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from migen.genlib.cdc import MultiReg, PulseSynchronizer
from misoc.interconnect.csr import *
from artiq.gateware.drtio.wrpll.si549 import Si549
from artiq.gateware.drtio.wrpll.ddmtd import DDMTD, Collector
from artiq.gateware.drtio.wrpll import thls, filters
class FrequencyCounter(Module, AutoCSR):
def __init__(self, timer_width=23, counter_width=23, domains=["helper", "rtio", "rtio_rx0"]):
for domain in domains:
name = "counter_" + domain
counter = CSRStatus(counter_width, name=name)
setattr(self, name, counter)
self.update_en = CSRStorage()
timer = Signal(timer_width)
timer_tick = Signal()
self.sync += Cat(timer, timer_tick).eq(timer + 1)
for domain in domains:
sync_domain = getattr(self.sync, domain)
divider = Signal(2)
sync_domain += divider.eq(divider + 1)
divided = Signal()
divided.attr.add("no_retiming")
sync_domain += divided.eq(divider[-1])
divided_sys = Signal()
self.specials += MultiReg(divided, divided_sys)
divided_sys_r = Signal()
divided_tick = Signal()
self.sync += divided_sys_r.eq(divided_sys)
self.comb += divided_tick.eq(divided_sys & ~divided_sys_r)
counter = Signal(counter_width)
counter_csr = getattr(self, "counter_" + domain)
self.sync += [
If(timer_tick,
If(self.update_en.storage, counter_csr.status.eq(counter)),
counter.eq(0),
).Else(
If(divided_tick, counter.eq(counter + 1))
)
]
class WRPLL(Module, AutoCSR):
def __init__(self, helper_clk_pads, main_dcxo_i2c, helper_dxco_i2c, ddmtd_inputs, N=15):
self.helper_reset = CSRStorage(reset=1)
self.collector_reset = CSRStorage(reset=1)
self.filter_reset = CSRStorage(reset=1)
self.adpll_offset_helper = CSRStorage(24)
self.adpll_offset_main = CSRStorage(24)
self.tag_arm = CSR()
self.main_diff_tag = CSRStatus(32)
self.helper_diff_tag = CSRStatus(32)
self.ref_tag = CSRStatus(N)
self.main_tag = CSRStatus(N)
main_diff_tag_32 = Signal((32, True))
helper_diff_tag_32 = Signal((32, True))
self.comb += [
self.main_diff_tag.status.eq(main_diff_tag_32),
self.helper_diff_tag.status.eq(helper_diff_tag_32)
]
self.clock_domains.cd_helper = ClockDomain()
self.clock_domains.cd_collector = ClockDomain()
self.clock_domains.cd_filter = ClockDomain()
self.helper_reset.storage.attr.add("no_retiming")
self.filter_reset.storage.attr.add("no_retiming")
self.specials += Instance("IBUFGDS",
i_I=helper_clk_pads.p, i_IB=helper_clk_pads.n,
o_O=self.cd_helper.clk)
self.comb += [
self.cd_collector.clk.eq(self.cd_collector.clk),
self.cd_filter.clk.eq(self.cd_helper.clk),
]
self.specials += [
AsyncResetSynchronizer(self.cd_helper, self.helper_reset.storage),
AsyncResetSynchronizer(self.cd_collector, self.collector_reset.storage),
AsyncResetSynchronizer(self.cd_filter, self.filter_reset.storage)
]
self.submodules.helper_dcxo = Si549(helper_dxco_i2c)
self.submodules.main_dcxo = Si549(main_dcxo_i2c)
# for diagnostics and PLL initialization
self.submodules.frequency_counter = FrequencyCounter()
ddmtd_counter = Signal(N)
self.sync.helper += ddmtd_counter.eq(ddmtd_counter + 1)
self.submodules.ddmtd_ref = DDMTD(ddmtd_counter, ddmtd_inputs.rec_clk)
self.submodules.ddmtd_main = DDMTD(ddmtd_counter, ddmtd_inputs.main_xo)
collector_cd = ClockDomainsRenamer("collector")
filter_cd = ClockDomainsRenamer("filter")
self.submodules.collector = collector_cd(Collector(N))
self.submodules.filter_helper = filter_cd(
thls.make(filters.helper, data_width=48))
self.submodules.filter_main = filter_cd(
thls.make(filters.main, data_width=48))
self.comb += [
self.collector.tag_ref.eq(self.ddmtd_ref.h_tag),
self.collector.ref_stb.eq(self.ddmtd_ref.h_tag_update),
self.collector.tag_main.eq(self.ddmtd_main.h_tag),
self.collector.main_stb.eq(self.ddmtd_main.h_tag_update)
]
collector_stb_ps = PulseSynchronizer("helper", "sys")
self.submodules += collector_stb_ps
self.sync.helper += collector_stb_ps.i.eq(self.collector.out_stb)
collector_stb_sys = Signal()
self.sync += collector_stb_sys.eq(collector_stb_ps.o)
main_diff_tag_sys = Signal((N+2, True))
helper_diff_tag_sys = Signal((N+2, True))
ref_tag_sys = Signal(N)
main_tag_sys = Signal(N)
self.specials += MultiReg(self.collector.out_main, main_diff_tag_sys)
self.specials += MultiReg(self.collector.out_helper, helper_diff_tag_sys)
self.specials += MultiReg(self.collector.tag_ref, ref_tag_sys)
self.specials += MultiReg(self.collector.tag_main, main_tag_sys)
self.sync += [
If(self.tag_arm.re & self.tag_arm.r, self.tag_arm.w.eq(1)),
If(collector_stb_sys,
self.tag_arm.w.eq(0),
If(self.tag_arm.w,
main_diff_tag_32.eq(main_diff_tag_sys),
helper_diff_tag_32.eq(helper_diff_tag_sys),
self.ref_tag.status.eq(ref_tag_sys),
self.main_tag.status.eq(main_tag_sys)
)
)
]
self.comb += [
self.filter_helper.input.eq(self.collector.out_helper << 22),
self.filter_helper.input_stb.eq(self.collector.out_stb),
self.filter_main.input.eq(self.collector.out_main),
self.filter_main.input_stb.eq(self.collector.out_stb)
]
self.sync.helper += [
self.helper_dcxo.adpll_stb.eq(self.filter_helper.output_stb),
self.helper_dcxo.adpll.eq(self.filter_helper.output + self.adpll_offset_helper.storage),
self.main_dcxo.adpll_stb.eq(self.filter_main.output_stb),
self.main_dcxo.adpll.eq(self.filter_main.output + self.adpll_offset_main.storage)
]

View File

@ -1,221 +0,0 @@
from migen import *
from migen.genlib.cdc import PulseSynchronizer, MultiReg
from migen.genlib.fsm import FSM
from misoc.interconnect.csr import *
class DDMTDSamplerExtFF(Module):
def __init__(self, ddmtd_inputs):
self.rec_clk = Signal()
self.main_xo = Signal()
# # #
# TODO: s/h timing at FPGA pads
if hasattr(ddmtd_inputs, "rec_clk"):
rec_clk_1 = ddmtd_inputs.rec_clk
else:
rec_clk_1 = Signal()
self.specials += Instance("IBUFDS",
i_I=ddmtd_inputs.rec_clk_p, i_IB=ddmtd_inputs.rec_clk_n,
o_O=rec_clk_1)
if hasattr(ddmtd_inputs, "main_xo"):
main_xo_1 = ddmtd_inputs.main_xo
else:
main_xo_1 = Signal()
self.specials += Instance("IBUFDS",
i_I=ddmtd_inputs.main_xo_p, i_IB=ddmtd_inputs.main_xo_n,
o_O=main_xo_1)
self.specials += [
Instance("FD", i_C=ClockSignal("helper"),
i_D=rec_clk_1, o_Q=self.rec_clk,
attr={("IOB", "TRUE")}),
Instance("FD", i_C=ClockSignal("helper"),
i_D=main_xo_1, o_Q=self.main_xo,
attr={("IOB", "TRUE")}),
]
class DDMTDSamplerGTP(Module):
def __init__(self, gtp, main_xo_pads):
self.rec_clk = Signal()
self.main_xo = Signal()
# # #
# Getting the main XO signal from IBUFDS_GTE2 is problematic because
# the transceiver PLL craps out if an improper clock signal is applied,
# so we are disabling the buffer until the clock is stable.
main_xo_se = Signal()
rec_clk_1 = Signal()
main_xo_1 = Signal()
self.specials += [
Instance("IBUFDS",
i_I=main_xo_pads.p, i_IB=main_xo_pads.n,
o_O=main_xo_se),
Instance("FD", i_C=ClockSignal("helper"),
i_D=gtp.cd_rtio_rx0.clk, o_Q=rec_clk_1,
attr={("DONT_TOUCH", "TRUE")}),
Instance("FD", i_C=ClockSignal("helper"),
i_D=rec_clk_1, o_Q=self.rec_clk,
attr={("DONT_TOUCH", "TRUE")}),
Instance("FD", i_C=ClockSignal("helper"),
i_D=main_xo_se, o_Q=main_xo_1,
attr={("IOB", "TRUE")}),
Instance("FD", i_C=ClockSignal("helper"),
i_D=main_xo_1, o_Q=self.main_xo,
attr={("DONT_TOUCH", "TRUE")}),
]
class DDMTDDeglitcherFirstEdge(Module):
def __init__(self, input_signal, blind_period=128):
self.detect = Signal()
self.tag_correction = 0
rising = Signal()
input_signal_r = Signal()
self.sync.helper += [
input_signal_r.eq(input_signal),
rising.eq(input_signal & ~input_signal_r)
]
blind_counter = Signal(max=blind_period)
self.sync.helper += [
If(blind_counter != 0, blind_counter.eq(blind_counter - 1)),
If(input_signal_r, blind_counter.eq(blind_period - 1)),
self.detect.eq(rising & (blind_counter == 0))
]
class DDMTD(Module):
def __init__(self, counter, input_signal):
# in helper clock domain
self.h_tag = Signal(len(counter))
self.h_tag_update = Signal()
# # #
deglitcher = DDMTDDeglitcherFirstEdge(input_signal)
self.submodules += deglitcher
self.sync.helper += [
self.h_tag_update.eq(0),
If(deglitcher.detect,
self.h_tag_update.eq(1),
self.h_tag.eq(counter + deglitcher.tag_correction)
)
]
class Collector(Module):
"""Generates loop filter inputs from DDMTD outputs.
The input to the main DCXO lock loop filter is the difference between the
reference and main tags after unwrapping (see below).
The input to the helper DCXO lock loop filter is the difference between the
current reference tag and the previous reference tag after unwrapping.
When the WR PLL is locked, the following ideally (no noise/jitter) obtain:
- f_main = f_ref
- f_helper = f_ref * 2^N/(2^N+1)
- f_beat = f_ref - f_helper = f_ref / (2^N + 1) (cycle time is: dt=1/f_beat)
- the reference and main DCXO tags are equal to each other at every cycle
(the main DCXO lock drives this difference to 0)
- the reference and main DCXO tags both have the same value at each cycle
(the tag difference for each DDMTD is given by
f_helper*dt = f_helper/f_beat = 2^N, which causes the N-bit DDMTD counter
to wrap around and come back to its previous value)
Note that we currently lock the frequency of the helper DCXO to the
reference clock, not it's phase. As a result, while the tag differences are
controlled, their absolute values are arbitrary. We could consider moving
the helper lock to a phase lock at some point in the future...
Since the DDMTD counter is only N bits, it is possible for tag values to
wrap around. This will happen frequently if the locked tags happens to be
near the edges of the counter, so that jitter can easily cause a phase wrap.
But, it can also easily happen during lock acquisition or other transients.
To avoid glitches in the output, we unwrap the tag differences. Currently
we do this in hardware, but we should consider extending the processor to
allow us to do it inside the filters. Since the processor uses wider
signals, this would significantly extend the overall glitch-free
range of the PLL and may aid lock acquisition.
"""
def __init__(self, N):
self.ref_stb = Signal()
self.main_stb = Signal()
self.tag_ref = Signal(N)
self.tag_main = Signal(N)
self.out_stb = Signal()
self.out_main = Signal((N+2, True))
self.out_helper = Signal((N+2, True))
self.out_tag_ref = Signal(N)
self.out_tag_main = Signal(N)
tag_ref_r = Signal(N)
tag_main_r = Signal(N)
main_tag_diff = Signal((N+2, True))
helper_tag_diff = Signal((N+2, True))
# # #
fsm = FSM(reset_state="IDLE")
self.submodules += fsm
fsm.act("IDLE",
NextValue(self.out_stb, 0),
If(self.ref_stb & self.main_stb,
NextValue(tag_ref_r, self.tag_ref),
NextValue(tag_main_r, self.tag_main),
NextState("DIFF")
).Elif(self.ref_stb,
NextValue(tag_ref_r, self.tag_ref),
NextState("WAITMAIN")
).Elif(self.main_stb,
NextValue(tag_main_r, self.tag_main),
NextState("WAITREF")
)
)
fsm.act("WAITREF",
If(self.ref_stb,
NextValue(tag_ref_r, self.tag_ref),
NextState("DIFF")
)
)
fsm.act("WAITMAIN",
If(self.main_stb,
NextValue(tag_main_r, self.tag_main),
NextState("DIFF")
)
)
fsm.act("DIFF",
NextValue(main_tag_diff, tag_main_r - tag_ref_r),
NextValue(helper_tag_diff, tag_ref_r - self.out_tag_ref),
NextState("UNWRAP")
)
fsm.act("UNWRAP",
If(main_tag_diff - self.out_main > 2**(N-1),
NextValue(main_tag_diff, main_tag_diff - 2**N)
).Elif(self.out_main - main_tag_diff > 2**(N-1),
NextValue(main_tag_diff, main_tag_diff + 2**N)
),
If(helper_tag_diff - self.out_helper > 2**(N-1),
NextValue(helper_tag_diff, helper_tag_diff - 2**N)
).Elif(self.out_helper - helper_tag_diff > 2**(N-1),
NextValue(helper_tag_diff, helper_tag_diff + 2**N)
),
NextState("OUTPUT")
)
fsm.act("OUTPUT",
NextValue(self.out_tag_ref, tag_ref_r),
NextValue(self.out_tag_main, tag_main_r),
NextValue(self.out_main, main_tag_diff),
NextValue(self.out_helper, helper_tag_diff),
NextValue(self.out_stb, 1),
NextState("IDLE")
)

View File

@ -1,61 +0,0 @@
helper_xn1 = 0
helper_xn2 = 0
helper_yn0 = 0
helper_yn1 = 0
helper_yn2 = 0
helper_out = 0
main_xn1 = 0
main_xn2 = 0
main_yn0 = 0
main_yn1 = 0
main_yn2 = 0
def helper(tag_diff):
global helper_xn1, helper_xn2, helper_yn0, \
helper_yn1, helper_yn2, helper_out
helper_xn0 = 0 - tag_diff # *(2**22)
helper_yr = 4294967296
helper_yn2 = helper_yn1
helper_yn1 = helper_yn0
helper_yn0 = (284885690 * (helper_xn0
+ (217319150 * helper_xn1 >> 44)
- (17591968725108 * helper_xn2 >> 44)
) >> 44
) + (35184372088832*helper_yn1 >> 44) - helper_yn2
helper_xn2 = helper_xn1
helper_xn1 = helper_xn0
helper_out = 268435456*helper_yn0 >> 44
helper_out = min(helper_out, helper_yr)
helper_out = max(helper_out, 0 - helper_yr)
return helper_out
def main(main_xn0):
global main_xn1, main_xn2, main_yn0, main_yn1, main_yn2
main_yr = 4294967296
main_yn2 = main_yn1
main_yn1 = main_yn0
main_yn0 = (
((133450380908*(((35184372088832*main_xn0) >> 44) +
((17592186044417*main_xn1) >> 44))) >> 44) +
((29455872930889*main_yn1) >> 44) -
((12673794781453*main_yn2) >> 44))
main_xn2 = main_xn1
main_xn1 = main_xn0
main_yn0 = min(main_yn0, main_yr)
main_yn0 = max(main_yn0, 0 - main_yr)
return main_yn0

View File

@ -1,340 +0,0 @@
from migen import *
from migen.genlib.fsm import *
from migen.genlib.cdc import MultiReg, PulseSynchronizer, BlindTransfer
from misoc.interconnect.csr import *
class I2CClockGen(Module):
def __init__(self, width):
self.load = Signal(width)
self.clk2x = Signal()
cnt = Signal.like(self.load)
self.comb += [
self.clk2x.eq(cnt == 0),
]
self.sync += [
If(self.clk2x,
cnt.eq(self.load),
).Else(
cnt.eq(cnt - 1),
)
]
class I2CMasterMachine(Module):
def __init__(self, clock_width):
self.scl = Signal(reset=1)
self.sda_o = Signal(reset=1)
self.sda_i = Signal()
self.submodules.cg = CEInserter()(I2CClockGen(clock_width))
self.start = Signal()
self.stop = Signal()
self.write = Signal()
self.ack = Signal()
self.data = Signal(8)
self.ready = Signal()
###
bits = Signal(4)
data = Signal(8)
fsm = CEInserter()(FSM("IDLE"))
self.submodules += fsm
fsm.act("IDLE",
self.ready.eq(1),
If(self.start,
NextState("START0"),
).Elif(self.stop,
NextState("STOP0"),
).Elif(self.write,
NextValue(bits, 8),
NextValue(data, self.data),
NextState("WRITE0")
)
)
fsm.act("START0",
NextValue(self.scl, 1),
NextState("START1")
)
fsm.act("START1",
NextValue(self.sda_o, 0),
NextState("IDLE")
)
fsm.act("STOP0",
NextValue(self.scl, 0),
NextState("STOP1")
)
fsm.act("STOP1",
NextValue(self.sda_o, 0),
NextState("STOP2")
)
fsm.act("STOP2",
NextValue(self.scl, 1),
NextState("STOP3")
)
fsm.act("STOP3",
NextValue(self.sda_o, 1),
NextState("IDLE")
)
fsm.act("WRITE0",
NextValue(self.scl, 0),
NextState("WRITE1")
)
fsm.act("WRITE1",
If(bits == 0,
NextValue(self.sda_o, 1),
NextState("READACK0"),
).Else(
NextValue(self.sda_o, data[7]),
NextState("WRITE2"),
)
)
fsm.act("WRITE2",
NextValue(self.scl, 1),
NextValue(data[1:], data[:-1]),
NextValue(bits, bits - 1),
NextState("WRITE0"),
)
fsm.act("READACK0",
NextValue(self.scl, 1),
NextState("READACK1"),
)
fsm.act("READACK1",
NextValue(self.ack, ~self.sda_i),
NextState("IDLE")
)
run = Signal()
idle = Signal()
self.comb += [
run.eq((self.start | self.stop | self.write) & self.ready),
idle.eq(~run & fsm.ongoing("IDLE")),
self.cg.ce.eq(~idle),
fsm.ce.eq(run | self.cg.clk2x),
]
class ADPLLProgrammer(Module):
def __init__(self):
self.i2c_divider = Signal(16)
self.i2c_address = Signal(7)
self.adpll = Signal(24)
self.stb = Signal()
self.busy = Signal()
self.nack = Signal()
self.scl = Signal()
self.sda_i = Signal()
self.sda_o = Signal()
self.scl.attr.add("no_retiming")
self.sda_o.attr.add("no_retiming")
# # #
master = I2CMasterMachine(16)
self.submodules += master
self.comb += [
master.cg.load.eq(self.i2c_divider),
self.scl.eq(master.scl),
master.sda_i.eq(self.sda_i),
self.sda_o.eq(master.sda_o)
]
fsm = FSM()
self.submodules += fsm
adpll = Signal.like(self.adpll)
fsm.act("IDLE",
If(self.stb,
NextValue(adpll, self.adpll),
NextState("START")
)
)
fsm.act("START",
master.start.eq(1),
If(master.ready, NextState("DEVADDRESS"))
)
fsm.act("DEVADDRESS",
master.data.eq(self.i2c_address << 1),
master.write.eq(1),
If(master.ready, NextState("REGADRESS"))
)
fsm.act("REGADRESS",
master.data.eq(231),
master.write.eq(1),
If(master.ready,
If(master.ack,
NextState("DATA0")
).Else(
self.nack.eq(1),
NextState("STOP")
)
)
)
fsm.act("DATA0",
master.data.eq(adpll[0:8]),
master.write.eq(1),
If(master.ready,
If(master.ack,
NextState("DATA1")
).Else(
self.nack.eq(1),
NextState("STOP")
)
)
)
fsm.act("DATA1",
master.data.eq(adpll[8:16]),
master.write.eq(1),
If(master.ready,
If(master.ack,
NextState("DATA2")
).Else(
self.nack.eq(1),
NextState("STOP")
)
)
)
fsm.act("DATA2",
master.data.eq(adpll[16:24]),
master.write.eq(1),
If(master.ready,
If(~master.ack, self.nack.eq(1)),
NextState("STOP")
)
)
fsm.act("STOP",
master.stop.eq(1),
If(master.ready,
If(~master.ack, self.nack.eq(1)),
NextState("IDLE")
)
)
self.comb += self.busy.eq(~fsm.ongoing("IDLE"))
def simulate_programmer():
from migen.sim.core import run_simulation
dut = ADPLLProgrammer()
def generator():
yield dut.i2c_divider.eq(4)
yield dut.i2c_address.eq(0x55)
yield
yield dut.adpll.eq(0x123456)
yield dut.stb.eq(1)
yield
yield dut.stb.eq(0)
yield
while (yield dut.busy):
yield
for _ in range(20):
yield
run_simulation(dut, generator(), vcd_name="tb.vcd")
class Si549(Module, AutoCSR):
def __init__(self, pads):
self.gpio_enable = CSRStorage(reset=1)
self.gpio_in = CSRStatus(2)
self.gpio_out = CSRStorage(2)
self.gpio_oe = CSRStorage(2)
self.i2c_divider = CSRStorage(16, reset=75)
self.i2c_address = CSRStorage(7)
self.errors = CSR(2)
# in helper clock domain
self.adpll = Signal(24)
self.adpll_stb = Signal()
# # #
programmer = ClockDomainsRenamer("helper")(ADPLLProgrammer())
self.submodules += programmer
self.i2c_divider.storage.attr.add("no_retiming")
self.i2c_address.storage.attr.add("no_retiming")
self.specials += [
MultiReg(self.i2c_divider.storage, programmer.i2c_divider, "helper"),
MultiReg(self.i2c_address.storage, programmer.i2c_address, "helper")
]
self.comb += [
programmer.adpll.eq(self.adpll),
programmer.stb.eq(self.adpll_stb)
]
self.gpio_enable.storage.attr.add("no_retiming")
self.gpio_out.storage.attr.add("no_retiming")
self.gpio_oe.storage.attr.add("no_retiming")
# SCL GPIO and mux
ts_scl = TSTriple(1)
self.specials += ts_scl.get_tristate(pads.scl)
status = Signal()
self.comb += self.gpio_in.status[0].eq(status)
self.specials += MultiReg(ts_scl.i, status)
self.comb += [
If(self.gpio_enable.storage,
ts_scl.o.eq(self.gpio_out.storage[0]),
ts_scl.oe.eq(self.gpio_oe.storage[0])
).Else(
ts_scl.o.eq(0),
ts_scl.oe.eq(~programmer.scl)
)
]
# SDA GPIO and mux
ts_sda = TSTriple(1)
self.specials += ts_sda.get_tristate(pads.sda)
status = Signal()
self.comb += self.gpio_in.status[1].eq(status)
self.specials += MultiReg(ts_sda.i, status)
self.comb += [
If(self.gpio_enable.storage,
ts_sda.o.eq(self.gpio_out.storage[1]),
ts_sda.oe.eq(self.gpio_oe.storage[1])
).Else(
ts_sda.o.eq(0),
ts_sda.oe.eq(~programmer.sda_o)
)
]
self.specials += MultiReg(ts_sda.i, programmer.sda_i, "helper")
# Error reporting
collision_cdc = BlindTransfer("helper", "sys")
self.submodules += collision_cdc
self.comb += collision_cdc.i.eq(programmer.stb & programmer.busy)
nack_cdc = PulseSynchronizer("helper", "sys")
self.submodules += nack_cdc
self.comb += nack_cdc.i.eq(programmer.nack)
for n, trig in enumerate([collision_cdc.o, nack_cdc.o]):
self.sync += [
If(self.errors.re & self.errors.r[n], self.errors.w[n].eq(0)),
If(trig, self.errors.w[n].eq(1))
]
if __name__ == "__main__":
simulate_programmer()

View File

@ -1,618 +0,0 @@
import inspect
import ast
from copy import copy
import operator
from functools import reduce
from collections import OrderedDict
from migen import *
from migen.genlib.fsm import *
class Isn:
def __init__(self, immediate=None, inputs=None, outputs=None):
if inputs is None:
inputs = []
if outputs is None:
outputs = []
self.immediate = immediate
self.inputs = inputs
self.outputs = outputs
def __repr__(self):
r = "<"
r += self.__class__.__name__
if self.immediate is not None:
r += " (" + str(self.immediate) + ")"
for inp in self.inputs:
r += " r" + str(inp)
if self.outputs:
r += " ->"
for outp in self.outputs:
r += " r" + str(outp)
r += ">"
return r
class NopIsn(Isn):
opcode = 0
class AddIsn(Isn):
opcode = 1
class SubIsn(Isn):
opcode = 2
class MulShiftIsn(Isn):
opcode = 3
# opcode = 4: MulShift with alternate shift
class MinIsn(Isn):
opcode = 5
class MaxIsn(Isn):
opcode = 6
class CopyIsn(Isn):
opcode = 7
class InputIsn(Isn):
opcode = 8
class OutputIsn(Isn):
opcode = 9
class EndIsn(Isn):
opcode = 10
class ASTCompiler:
def __init__(self):
self.program = []
self.data = []
self.next_ssa_reg = -1
self.constants = dict()
self.names = dict()
self.globals = OrderedDict()
def get_ssa_reg(self):
r = self.next_ssa_reg
self.next_ssa_reg -= 1
return r
def add_global(self, name):
if name not in self.globals:
r = len(self.data)
self.data.append(0)
self.names[name] = r
self.globals[name] = r
def input(self, name):
target = self.get_ssa_reg()
self.program.append(InputIsn(outputs=[target]))
self.names[name] = target
def emit(self, node):
if isinstance(node, ast.BinOp):
if isinstance(node.op, ast.RShift):
if not isinstance(node.left, ast.BinOp) or not isinstance(node.left.op, ast.Mult):
raise NotImplementedError
if not isinstance(node.right, ast.Num):
raise NotImplementedError
left = self.emit(node.left.left)
right = self.emit(node.left.right)
cons = lambda **kwargs: MulShiftIsn(immediate=node.right.n, **kwargs)
else:
left = self.emit(node.left)
right = self.emit(node.right)
if isinstance(node.op, ast.Add):
cons = AddIsn
elif isinstance(node.op, ast.Sub):
cons = SubIsn
elif isinstance(node.op, ast.Mult):
cons = lambda **kwargs: MulShiftIsn(immediate=0, **kwargs)
else:
raise NotImplementedError
output = self.get_ssa_reg()
self.program.append(cons(inputs=[left, right], outputs=[output]))
return output
elif isinstance(node, ast.Call):
if not isinstance(node.func, ast.Name):
raise NotImplementedError
funcname = node.func.id
if node.keywords:
raise NotImplementedError
inputs = [self.emit(x) for x in node.args]
if funcname == "min":
cons = MinIsn
elif funcname == "max":
cons = MaxIsn
else:
raise NotImplementedError
output = self.get_ssa_reg()
self.program.append(cons(inputs=inputs, outputs=[output]))
return output
elif isinstance(node, (ast.Num, ast.UnaryOp)):
if isinstance(node, ast.UnaryOp):
if not isinstance(node.operand, ast.Num):
raise NotImplementedError
if isinstance(node.op, ast.UAdd):
transform = lambda x: x
elif isinstance(node.op, ast.USub):
transform = operator.neg
elif isinstance(node.op, ast.Invert):
transform = operator.invert
else:
raise NotImplementedError
node = node.operand
else:
transform = lambda x: x
n = transform(node.n)
if n in self.constants:
return self.constants[n]
else:
r = len(self.data)
self.data.append(n)
self.constants[n] = r
return r
elif isinstance(node, ast.Name):
return self.names[node.id]
elif isinstance(node, ast.Assign):
output = self.emit(node.value)
for target in node.targets:
assert isinstance(target, ast.Name)
self.names[target.id] = output
elif isinstance(node, ast.Return):
value = self.emit(node.value)
self.program.append(OutputIsn(inputs=[value]))
elif isinstance(node, ast.Global):
pass
else:
raise NotImplementedError
class Processor:
def __init__(self, data_width=32, multiplier_stages=2):
self.data_width = data_width
self.multiplier_stages = multiplier_stages
self.multiplier_shifts = []
self.program_rom_size = None
self.data_ram_size = None
self.opcode_bits = 4
self.reg_bits = None
def get_instruction_latency(self, isn):
return {
AddIsn: 2,
SubIsn: 2,
MulShiftIsn: 1 + self.multiplier_stages,
MinIsn: 2,
MaxIsn: 2,
CopyIsn: 1,
InputIsn: 1
}[isn.__class__]
def encode_instruction(self, isn, exit):
opcode = isn.opcode
if isn.immediate is not None and not isinstance(isn, MulShiftIsn):
r0 = isn.immediate
if len(isn.inputs) >= 1:
r1 = isn.inputs[0]
else:
r1 = 0
else:
if len(isn.inputs) >= 1:
r0 = isn.inputs[0]
else:
r0 = 0
if len(isn.inputs) >= 2:
r1 = isn.inputs[1]
else:
r1 = 0
r = 0
for value, bits in ((exit, self.reg_bits), (r1, self.reg_bits), (r0, self.reg_bits), (opcode, self.opcode_bits)):
r <<= bits
r |= value
return r
def instruction_bits(self):
return 3*self.reg_bits + self.opcode_bits
def implement(self, program, data):
return ProcessorImpl(self, program, data)
class Scheduler:
def __init__(self, processor, reserved_data, program):
self.processor = processor
self.reserved_data = reserved_data
self.used_registers = set(range(self.reserved_data))
self.exits = dict()
self.program = program
self.remaining = copy(program)
self.output = []
def allocate_register(self):
r = min(set(range(max(self.used_registers) + 2)) - self.used_registers)
self.used_registers.add(r)
return r
def free_register(self, r):
assert r >= self.reserved_data
self.used_registers.discard(r)
def find_inputs(self, cycle, isn):
mapped_inputs = []
for inp in isn.inputs:
if inp >= 0:
mapped_inputs.append(inp)
else:
found = False
for i in range(cycle):
if i in self.exits:
r, rm = self.exits[i]
if r == inp:
mapped_inputs.append(rm)
found = True
break
if not found:
return None
return mapped_inputs
def schedule_one(self, isn):
cycle = len(self.output)
mapped_inputs = self.find_inputs(cycle, isn)
if mapped_inputs is None:
return False
if isn.outputs:
# check that exit slot is free
latency = self.processor.get_instruction_latency(isn)
exit = cycle + latency
if exit in self.exits:
return False
# avoid RAW hazard with global writeback
for output in isn.outputs:
if output >= 0:
for risn in self.remaining:
for inp in risn.inputs:
if inp == output:
return False
# Instruction can be scheduled
self.remaining.remove(isn)
for inp, minp in zip(isn.inputs, mapped_inputs):
can_free = inp < 0 and all(inp != rinp for risn in self.remaining for rinp in risn.inputs)
if can_free:
self.free_register(minp)
if isn.outputs:
assert len(isn.outputs) == 1
if isn.outputs[0] < 0:
output = self.allocate_register()
else:
output = isn.outputs[0]
self.exits[exit] = (isn.outputs[0], output)
self.output.append(isn.__class__(immediate=isn.immediate, inputs=mapped_inputs))
return True
def schedule(self):
while self.remaining:
success = False
for isn in self.remaining:
if self.schedule_one(isn):
success = True
break
if not success:
self.output.append(NopIsn())
self.output += [NopIsn()]*(max(self.exits.keys()) - len(self.output) + 1)
return self.output
class CompiledProgram:
def __init__(self, processor, program, exits, data, glbs):
self.processor = processor
self.program = program
self.exits = exits
self.data = data
self.globals = glbs
def pretty_print(self):
for cycle, isn in enumerate(self.program):
l = "{:4d} {:15}".format(cycle, str(isn))
if cycle in self.exits:
l += " -> r{}".format(self.exits[cycle])
print(l)
def dimension_processor(self):
self.processor.program_rom_size = len(self.program)
self.processor.data_ram_size = len(self.data)
self.processor.reg_bits = (self.processor.data_ram_size - 1).bit_length()
for isn in self.program:
if isinstance(isn, MulShiftIsn) and isn.immediate not in self.processor.multiplier_shifts:
self.processor.multiplier_shifts.append(isn.immediate)
def encode(self):
r = []
for i, isn in enumerate(self.program):
exit = self.exits.get(i, 0)
r.append(self.processor.encode_instruction(isn, exit))
return r
def compile(processor, function):
node = ast.parse(inspect.getsource(function))
assert isinstance(node, ast.Module)
assert len(node.body) == 1
node = node.body[0]
assert isinstance(node, ast.FunctionDef)
assert len(node.args.args) == 1
arg = node.args.args[0].arg
body = node.body
astcompiler = ASTCompiler()
for node in body:
if isinstance(node, ast.Global):
for name in node.names:
astcompiler.add_global(name)
arg_r = astcompiler.input(arg)
for node in body:
astcompiler.emit(node)
if isinstance(node, ast.Return):
break
for glbl, location in astcompiler.globals.items():
new_location = astcompiler.names[glbl]
if new_location != location:
astcompiler.program.append(CopyIsn(inputs=[new_location], outputs=[location]))
scheduler = Scheduler(processor, len(astcompiler.data), astcompiler.program)
scheduler.schedule()
program = copy(scheduler.output)
program.append(EndIsn())
max_reg = max(max(max(isn.inputs + [0]) for isn in program), max(v[1] for k, v in scheduler.exits.items()))
return CompiledProgram(
processor=processor,
program=program,
exits={k: v[1] for k, v in scheduler.exits.items()},
data=astcompiler.data + [0]*(max_reg - len(astcompiler.data) + 1),
glbs=astcompiler.globals)
class BaseUnit(Module):
def __init__(self, data_width):
self.stb_i = Signal()
self.i0 = Signal((data_width, True))
self.i1 = Signal((data_width, True))
self.stb_o = Signal()
self.o = Signal((data_width, True))
class NopUnit(BaseUnit):
pass
class OpUnit(BaseUnit):
def __init__(self, op, data_width, stages, op_data_width=None):
BaseUnit.__init__(self, data_width)
# work around Migen's mishandling of Verilog's cretinous operator width rules
if op_data_width is None:
op_data_width = data_width
if stages > 1:
# Vivado backward retiming for DSP does not work correctly if DSP inputs
# are not registered.
i0 = Signal.like(self.i0)
i1 = Signal.like(self.i1)
stb_i = Signal()
self.sync += [
i0.eq(self.i0),
i1.eq(self.i1),
stb_i.eq(self.stb_i)
]
output_stages = stages - 1
else:
i0, i1, stb_i = self.i0, self.i1, self.stb_i
output_stages = stages
o = Signal((op_data_width, True))
self.comb += o.eq(op(i0, i1))
stb_o = stb_i
for i in range(output_stages):
n_o = Signal((data_width, True))
if stages > 1:
n_o.attr.add(("retiming_backward", 1))
n_stb_o = Signal()
self.sync += [
n_o.eq(o),
n_stb_o.eq(stb_o)
]
o = n_o
stb_o = n_stb_o
self.comb += [
self.o.eq(o),
self.stb_o.eq(stb_o)
]
class SelectUnit(BaseUnit):
def __init__(self, op, data_width):
BaseUnit.__init__(self, data_width)
self.sync += [
self.stb_o.eq(self.stb_i),
If(op(self.i0, self.i1),
self.o.eq(self.i0)
).Else(
self.o.eq(self.i1)
)
]
class CopyUnit(BaseUnit):
def __init__(self, data_width):
BaseUnit.__init__(self, data_width)
self.comb += [
self.stb_o.eq(self.stb_i),
self.o.eq(self.i0)
]
class InputUnit(BaseUnit):
def __init__(self, data_width, input_stb, input):
BaseUnit.__init__(self, data_width)
self.buffer = Signal(data_width)
self.comb += [
self.stb_o.eq(self.stb_i),
self.o.eq(self.buffer)
]
class OutputUnit(BaseUnit):
def __init__(self, data_width, output_stb, output):
BaseUnit.__init__(self, data_width)
self.sync += [
output_stb.eq(self.stb_i),
output.eq(self.i0)
]
class ProcessorImpl(Module):
def __init__(self, pd, program, data):
self.input_stb = Signal()
self.input = Signal((pd.data_width, True))
self.output_stb = Signal()
self.output = Signal((pd.data_width, True))
self.busy = Signal()
# # #
program_mem = Memory(pd.instruction_bits(), pd.program_rom_size, init=program)
data_mem0 = Memory(pd.data_width, pd.data_ram_size, init=data)
data_mem1 = Memory(pd.data_width, pd.data_ram_size, init=data)
self.specials += program_mem, data_mem0, data_mem1
pc = Signal(pd.instruction_bits())
pc_next = Signal.like(pc)
pc_en = Signal()
self.sync += pc.eq(pc_next)
self.comb += [
If(pc_en,
pc_next.eq(pc + 1)
).Else(
pc_next.eq(0)
)
]
program_mem_port = program_mem.get_port()
self.specials += program_mem_port
self.comb += program_mem_port.adr.eq(pc_next)
s = 0
opcode = Signal(pd.opcode_bits)
self.comb += opcode.eq(program_mem_port.dat_r[s:s+pd.opcode_bits])
s += pd.opcode_bits
r0 = Signal(pd.reg_bits)
self.comb += r0.eq(program_mem_port.dat_r[s:s+pd.reg_bits])
s += pd.reg_bits
r1 = Signal(pd.reg_bits)
self.comb += r1.eq(program_mem_port.dat_r[s:s+pd.reg_bits])
s += pd.reg_bits
exit = Signal(pd.reg_bits)
self.comb += exit.eq(program_mem_port.dat_r[s:s+pd.reg_bits])
data_read_port0 = data_mem0.get_port()
data_read_port1 = data_mem1.get_port()
self.specials += data_read_port0, data_read_port1
self.comb += [
data_read_port0.adr.eq(r0),
data_read_port1.adr.eq(r1)
]
data_write_port = data_mem0.get_port(write_capable=True)
data_write_port_dup = data_mem1.get_port(write_capable=True)
self.specials += data_write_port, data_write_port_dup
self.comb += [
data_write_port_dup.we.eq(data_write_port.we),
data_write_port_dup.adr.eq(data_write_port.adr),
data_write_port_dup.dat_w.eq(data_write_port.dat_w),
data_write_port.adr.eq(exit)
]
nop = NopUnit(pd.data_width)
adder = OpUnit(operator.add, pd.data_width, 1)
subtractor = OpUnit(operator.sub, pd.data_width, 1)
if pd.multiplier_shifts:
if len(pd.multiplier_shifts) != 1:
raise NotImplementedError
multiplier = OpUnit(lambda a, b: a * b >> pd.multiplier_shifts[0],
pd.data_width, pd.multiplier_stages, op_data_width=2*pd.data_width)
else:
multiplier = NopUnit(pd.data_width)
minu = SelectUnit(operator.lt, pd.data_width)
maxu = SelectUnit(operator.gt, pd.data_width)
copier = CopyUnit(pd.data_width)
inu = InputUnit(pd.data_width, self.input_stb, self.input)
outu = OutputUnit(pd.data_width, self.output_stb, self.output)
units = [nop, adder, subtractor, multiplier, minu, maxu, copier, inu, outu]
self.submodules += units
for unit in units:
self.sync += unit.stb_i.eq(0)
self.comb += [
unit.i0.eq(data_read_port0.dat_r),
unit.i1.eq(data_read_port1.dat_r),
If(unit.stb_o,
data_write_port.we.eq(1),
data_write_port.dat_w.eq(unit.o)
)
]
decode_table = [
(NopIsn.opcode, nop),
(AddIsn.opcode, adder),
(SubIsn.opcode, subtractor),
(MulShiftIsn.opcode, multiplier),
(MulShiftIsn.opcode + 1, multiplier),
(MinIsn.opcode, minu),
(MaxIsn.opcode, maxu),
(CopyIsn.opcode, copier),
(InputIsn.opcode, inu),
(OutputIsn.opcode, outu)
]
for allocated_opcode, unit in decode_table:
self.sync += If(pc_en & (opcode == allocated_opcode), unit.stb_i.eq(1))
fsm = FSM()
self.submodules += fsm
fsm.act("IDLE",
pc_en.eq(0),
NextValue(inu.buffer, self.input),
If(self.input_stb, NextState("PROCESSING"))
)
fsm.act("PROCESSING",
self.busy.eq(1),
pc_en.eq(1),
If(opcode == EndIsn.opcode,
pc_en.eq(0),
NextState("IDLE")
)
)
def make(function, **kwargs):
proc = Processor(**kwargs)
cp = compile(proc, function)
cp.dimension_processor()
return proc.implement(cp.encode(), cp.data)

View File

@ -196,25 +196,25 @@ class Sampler(_EEM):
ios = [ ios = [
("sampler{}_adc_spi_p".format(eem), 0, ("sampler{}_adc_spi_p".format(eem), 0,
Subsignal("clk", Pins(_eem_pin(eem, 0, "p"))), Subsignal("clk", Pins(_eem_pin(eem, 0, "p"))),
Subsignal("miso", Pins(_eem_pin(eem, 1, "p"))), Subsignal("miso", Pins(_eem_pin(eem, 1, "p")), Misc("DIFF_TERM=TRUE")),
iostandard(eem), iostandard(eem),
), ),
("sampler{}_adc_spi_n".format(eem), 0, ("sampler{}_adc_spi_n".format(eem), 0,
Subsignal("clk", Pins(_eem_pin(eem, 0, "n"))), Subsignal("clk", Pins(_eem_pin(eem, 0, "n"))),
Subsignal("miso", Pins(_eem_pin(eem, 1, "n"))), Subsignal("miso", Pins(_eem_pin(eem, 1, "n")), Misc("DIFF_TERM=TRUE")),
iostandard(eem), iostandard(eem),
), ),
("sampler{}_pgia_spi_p".format(eem), 0, ("sampler{}_pgia_spi_p".format(eem), 0,
Subsignal("clk", Pins(_eem_pin(eem, 4, "p"))), Subsignal("clk", Pins(_eem_pin(eem, 4, "p"))),
Subsignal("mosi", Pins(_eem_pin(eem, 5, "p"))), Subsignal("mosi", Pins(_eem_pin(eem, 5, "p"))),
Subsignal("miso", Pins(_eem_pin(eem, 6, "p"))), Subsignal("miso", Pins(_eem_pin(eem, 6, "p")), Misc("DIFF_TERM=TRUE")),
Subsignal("cs_n", Pins(_eem_pin(eem, 7, "p"))), Subsignal("cs_n", Pins(_eem_pin(eem, 7, "p"))),
iostandard(eem), iostandard(eem),
), ),
("sampler{}_pgia_spi_n".format(eem), 0, ("sampler{}_pgia_spi_n".format(eem), 0,
Subsignal("clk", Pins(_eem_pin(eem, 4, "n"))), Subsignal("clk", Pins(_eem_pin(eem, 4, "n"))),
Subsignal("mosi", Pins(_eem_pin(eem, 5, "n"))), Subsignal("mosi", Pins(_eem_pin(eem, 5, "n"))),
Subsignal("miso", Pins(_eem_pin(eem, 6, "n"))), Subsignal("miso", Pins(_eem_pin(eem, 6, "n")), Misc("DIFF_TERM=TRUE")),
Subsignal("cs_n", Pins(_eem_pin(eem, 7, "n"))), Subsignal("cs_n", Pins(_eem_pin(eem, 7, "n"))),
iostandard(eem), iostandard(eem),
), ),
@ -570,14 +570,14 @@ class Mirny(_EEM):
("mirny{}_spi_p".format(eem), 0, ("mirny{}_spi_p".format(eem), 0,
Subsignal("clk", Pins(_eem_pin(eem, 0, "p"))), Subsignal("clk", Pins(_eem_pin(eem, 0, "p"))),
Subsignal("mosi", Pins(_eem_pin(eem, 1, "p"))), Subsignal("mosi", Pins(_eem_pin(eem, 1, "p"))),
Subsignal("miso", Pins(_eem_pin(eem, 2, "p"))), Subsignal("miso", Pins(_eem_pin(eem, 2, "p")), Misc("DIFF_TERM=TRUE")),
Subsignal("cs_n", Pins(_eem_pin(eem, 3, "p"))), Subsignal("cs_n", Pins(_eem_pin(eem, 3, "p"))),
iostandard(eem), iostandard(eem),
), ),
("mirny{}_spi_n".format(eem), 0, ("mirny{}_spi_n".format(eem), 0,
Subsignal("clk", Pins(_eem_pin(eem, 0, "n"))), Subsignal("clk", Pins(_eem_pin(eem, 0, "n"))),
Subsignal("mosi", Pins(_eem_pin(eem, 1, "n"))), Subsignal("mosi", Pins(_eem_pin(eem, 1, "n"))),
Subsignal("miso", Pins(_eem_pin(eem, 2, "n"))), Subsignal("miso", Pins(_eem_pin(eem, 2, "n")), Misc("DIFF_TERM=TRUE")),
Subsignal("cs_n", Pins(_eem_pin(eem, 3, "n"))), Subsignal("cs_n", Pins(_eem_pin(eem, 3, "n"))),
iostandard(eem), iostandard(eem),
), ),

View File

@ -19,31 +19,27 @@ from jesd204b.core import JESD204BCoreTXControl
class UltrascaleCRG(Module, AutoCSR): class UltrascaleCRG(Module, AutoCSR):
linerate = int(6e9) # linerate = 20*data_rate*4/8 = data_rate*10 linerate = int(10e9) # linerate = 20*data_rate*4/8 = data_rate*10
# data_rate = dac_rate/interp_factor # data_rate = dac_rate/interp_factor
refclk_freq = int(150e6) refclk_freq = int(250e6)
fabric_freq = int(125e6) fabric_freq = int(125e6)
def __init__(self, platform, use_rtio_clock=False): def __init__(self, platform):
self.jreset = CSRStorage(reset=1) self.jreset = CSRStorage(reset=1)
self.refclk = Signal() self.refclk = Signal()
self.clock_domains.cd_jesd = ClockDomain() self.clock_domains.cd_jesd = ClockDomain()
refclk2 = Signal()
refclk_pads = platform.request("dac_refclk", 0) refclk_pads = platform.request("dac_refclk", 0)
platform.add_period_constraint(refclk_pads.p, 1e9/self.refclk_freq) platform.add_period_constraint(refclk_pads.p, 1e9/self.refclk_freq)
self.specials += [ self.specials += [
Instance("IBUFDS_GTE3", i_CEB=0, p_REFCLK_HROW_CK_SEL=0b00, Instance("IBUFDS_GTE3", i_CEB=0, p_REFCLK_HROW_CK_SEL=0b01,
i_I=refclk_pads.p, i_IB=refclk_pads.n, i_I=refclk_pads.p, i_IB=refclk_pads.n,
o_O=self.refclk, o_ODIV2=refclk2), o_O=self.refclk),
AsyncResetSynchronizer(self.cd_jesd, self.jreset.storage),
] ]
if use_rtio_clock:
self.cd_jesd.clk.attr.add("keep") self.cd_jesd.clk.attr.add("keep")
self.comb += self.cd_jesd.clk.eq(ClockSignal("rtio")) self.comb += self.cd_jesd.clk.eq(ClockSignal("rtio"))
else: self.specials += AsyncResetSynchronizer(self.cd_jesd, self.jreset.storage)
self.specials += Instance("BUFG_GT", i_I=refclk2, o_O=self.cd_jesd.clk)
PhyPads = namedtuple("PhyPads", "txp txn") PhyPads = namedtuple("PhyPads", "txp txn")
@ -61,7 +57,7 @@ class UltrascaleTX(Module, AutoCSR):
"qpll": JESD204BGTHQuadPLL "qpll": JESD204BGTHQuadPLL
}[pll_type] }[pll_type]
ps = JESD204BPhysicalSettings(l=8, m=4, n=16, np=16) ps = JESD204BPhysicalSettings(l=8, m=4, n=16, np=16)
ts = JESD204BTransportSettings(f=2, s=2, k=16, cs=0) ts = JESD204BTransportSettings(f=2, s=2, k=32, cs=0)
settings = JESD204BSettings(ps, ts, did=0x5a, bid=0x5) settings = JESD204BSettings(ps, ts, did=0x5a, bid=0x5)
jesd_pads = platform.request("dac_jesd", dac) jesd_pads = platform.request("dac_jesd", dac)
@ -142,7 +138,7 @@ class UltrascaleTX(Module, AutoCSR):
pll, phy.transmitter, **phy_channel_cfg) pll, phy.transmitter, **phy_channel_cfg)
self.submodules.core = JESD204BCoreTX( self.submodules.core = JESD204BCoreTX(
phys, settings, converter_data_width=64) phys, settings, converter_data_width=128, tx_half=tx_half)
self.submodules.control = JESD204BCoreTXControl(self.core) self.submodules.control = JESD204BCoreTXControl(self.core)
self.core.register_jsync(platform.request("dac_sync", dac)) self.core.register_jsync(platform.request("dac_sync", dac))
@ -166,7 +162,7 @@ class DDMTDEdgeDetector(Module):
# See "Digital femtosecond time difference circuit for CERN's timing system" # See "Digital femtosecond time difference circuit for CERN's timing system"
# by P. Moreira and I. Darwazeh # by P. Moreira and I. Darwazeh
class DDMTD(Module, AutoCSR): class DDMTD(Module, AutoCSR):
def __init__(self, input_pads, rtio_clk_freq=150e6): def __init__(self, input_pads, rtio_clk_freq=125e6):
N = 64 N = 64
self.reset = CSRStorage(reset=1) self.reset = CSRStorage(reset=1)
self.locked = CSRStatus() self.locked = CSRStatus()
@ -189,7 +185,7 @@ class DDMTD(Module, AutoCSR):
i_RST=self.reset.storage, i_RST=self.reset.storage,
o_LOCKED=helper_locked, o_LOCKED=helper_locked,
# VCO at 1200MHz with 150MHz RTIO frequency # VCO at 1000MHz/1200MHz with 125MHz/150MHz RTIO frequency
p_CLKFBOUT_MULT_F=8.0, p_CLKFBOUT_MULT_F=8.0,
p_DIVCLK_DIVIDE=1, p_DIVCLK_DIVIDE=1,

View File

@ -110,7 +110,7 @@ class RawSlicer(Module):
for i in range(out_size)})), for i in range(out_size)})),
If(shift_buf, Case(self.source_consume, If(shift_buf, Case(self.source_consume,
{i: buf.eq(buf[i*g:]) {i: buf.eq(buf[i*g:])
for i in range(out_size)})), for i in range(out_size + 1)})),
] ]
fsm = FSM(reset_state="FETCH") fsm = FSM(reset_state="FETCH")

View File

@ -0,0 +1,77 @@
from migen import *
from migen.genlib.coding import PriorityEncoder
from artiq.gateware.rtio import rtlink
def _mk_edges(w, direction):
l = [(1 << i) - 1 for i in range(w)]
if direction == "rising":
l = [((1 << w) - 1) ^ x for x in l]
elif direction == "falling":
pass
else:
raise ValueError
return l
class _SerdesSquareWaveDriver(Module):
def __init__(self, serdes_o, rtio_freq, wave_freq):
assert wave_freq <= rtio_freq
serdes_width = len(serdes_o)
assert serdes_width & (serdes_width-1) == 0 # serdes_width must be 2**n
edges = Array(_mk_edges(serdes_width, "rising"))
edges_n = Array(_mk_edges(serdes_width, "falling"))
phase_accumulator = Signal(32)
tuning_word = int((wave_freq/rtio_freq) * 2**32)
fine_ts = Signal() # indicates which rtiox period within the
# current rtio period should the edge be changed
logic_level = Signal(reset=1)
logic_level_d = Signal(reset=1)
self.comb += [
fine_ts.eq(phase_accumulator[-log2_int(serdes_width):]),
logic_level.eq(~phase_accumulator[-1]),
]
# Using CD rio such that RtioInitRequest
# resets the phase accumulator and logic level registers
# (Refer to :class:`artiq.gateware.rtio.core.Core`)
self.sync.rio += [
logic_level_d.eq(logic_level),
If(~logic_level_d & logic_level,
serdes_o.eq(edges[fine_ts]),
).Elif(logic_level_d & ~logic_level,
serdes_o.eq(edges_n[fine_ts]),
).Else(
serdes_o.eq(Replicate(logic_level_d, serdes_width)),
),
phase_accumulator.eq(phase_accumulator + tuning_word),
]
SEDRES_DRIVER_TYPES = {
"square_wave": _SerdesSquareWaveDriver,
}
class Output(Module):
def __init__(self, serdes, driver_type, **kwargs):
assert driver_type in SEDRES_DRIVER_TYPES.keys()
# Include an unused, dummy rtlink interface just to consume an RTIO channel
self.rtlink = rtlink.Interface(
rtlink.OInterface(1, fine_ts_width=log2_int(len(serdes.o))))
self.probes = [Signal()]
self.overrides = [Signal(), Signal()]
# # #
self.submodules += SEDRES_DRIVER_TYPES[driver_type](
serdes.o, **kwargs)
class InOut(Module):
def __init__(self, serdes):
raise NotImplementedError()

View File

@ -1,6 +1,6 @@
from migen import * from migen import *
from artiq.gateware.rtio.phy import ttl_serdes_generic from artiq.gateware.rtio.phy import ttl_serdes_generic, ttl_serdes_nortio
class _OSERDESE3(Module): class _OSERDESE3(Module):
@ -99,3 +99,23 @@ class InOut(ttl_serdes_generic.InOut):
i_INTERMDISABLE=~serdes.t_out, i_INTERMDISABLE=~serdes.t_out,
i_I=serdes.ser_out, o_O=serdes.ser_in, i_T=serdes.t_out, i_I=serdes.ser_out, o_O=serdes.ser_in, i_T=serdes.t_out,
io_IO=pad, io_IOB=pad_n) io_IO=pad, io_IOB=pad_n)
class CustomOutput(ttl_serdes_nortio.Output):
def __init__(self, dw, pad, pad_n=None, dci=False, **kwargs):
serdes = _OSERDESE3(dw)
self.submodules += serdes
ttl_serdes_nortio.Output.__init__(self, serdes, **kwargs)
if pad_n is None:
self.comb += pad.eq(serdes.ser_out)
else:
self.specials += Instance("IOBUFDS",
i_I=serdes.ser_out,
i_T=serdes.t_out,
io_IO=pad, io_IOB=pad_n)
class CustomInOut(ttl_serdes_nortio.InOut):
def __init__(self, dw, pad, pad_n=None, dci=False, **kwargs):
raise NotImplementedError()

View File

@ -20,7 +20,6 @@ from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, edge_counter
from artiq.gateware import eem from artiq.gateware import eem
from artiq.gateware.drtio.transceiver import gtp_7series from artiq.gateware.drtio.transceiver import gtp_7series
from artiq.gateware.drtio.siphaser import SiPhaser7Series from artiq.gateware.drtio.siphaser import SiPhaser7Series
from artiq.gateware.drtio.wrpll import WRPLL, DDMTDSamplerGTP
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
from artiq.gateware.drtio import * from artiq.gateware.drtio import *
from artiq.build_soc import * from artiq.build_soc import *
@ -311,6 +310,9 @@ class MasterBase(MiniSoC, AMPSoC):
platform = self.platform platform = self.platform
if platform.hw_rev == "v2.0": if platform.hw_rev == "v2.0":
self.submodules.error_led = gpio.GPIOOut(Cat(
self.platform.request("error_led")))
self.csr_devices.append("error_led")
self.submodules += SMAClkinForward(platform) self.submodules += SMAClkinForward(platform)
i2c = self.platform.request("i2c") i2c = self.platform.request("i2c")
@ -445,7 +447,10 @@ class MasterBase(MiniSoC, AMPSoC):
self.specials += Instance("IBUFDS_GTE2", self.specials += Instance("IBUFDS_GTE2",
i_CEB=self.disable_cdr_clk_ibuf, i_CEB=self.disable_cdr_clk_ibuf,
i_I=cdr_clk_clean.p, i_IB=cdr_clk_clean.n, i_I=cdr_clk_clean.p, i_IB=cdr_clk_clean.n,
o_O=cdr_clk_clean_buf) o_O=cdr_clk_clean_buf,
p_CLKCM_CFG="TRUE",
p_CLKRCV_TRST="TRUE",
p_CLKSWING_CFG=3)
# Note precisely the rules Xilinx made up: # Note precisely the rules Xilinx made up:
# refclksel=0b001 GTREFCLK0 selected # refclksel=0b001 GTREFCLK0 selected
# refclksel=0b010 GTREFCLK1 selected # refclksel=0b010 GTREFCLK1 selected
@ -472,7 +477,7 @@ class SatelliteBase(BaseSoC):
} }
mem_map.update(BaseSoC.mem_map) mem_map.update(BaseSoC.mem_map)
def __init__(self, rtio_clk_freq=125e6, enable_sata=False, *, with_wrpll=False, gateware_identifier_str=None, **kwargs): def __init__(self, rtio_clk_freq=125e6, enable_sata=False, *, gateware_identifier_str=None, **kwargs):
BaseSoC.__init__(self, BaseSoC.__init__(self,
cpu_type="or1k", cpu_type="or1k",
sdram_controller_type="minicon", sdram_controller_type="minicon",
@ -482,6 +487,11 @@ class SatelliteBase(BaseSoC):
platform = self.platform platform = self.platform
if self.platform.hw_rev == "v2.0":
self.submodules.error_led = gpio.GPIOOut(Cat(
self.platform.request("error_led")))
self.csr_devices.append("error_led")
disable_cdr_clk_ibuf = Signal(reset=1) disable_cdr_clk_ibuf = Signal(reset=1)
disable_cdr_clk_ibuf.attr.add("no_retiming") disable_cdr_clk_ibuf.attr.add("no_retiming")
if self.platform.hw_rev == "v2.0": if self.platform.hw_rev == "v2.0":
@ -492,7 +502,10 @@ class SatelliteBase(BaseSoC):
self.specials += Instance("IBUFDS_GTE2", self.specials += Instance("IBUFDS_GTE2",
i_CEB=disable_cdr_clk_ibuf, i_CEB=disable_cdr_clk_ibuf,
i_I=cdr_clk_clean.p, i_IB=cdr_clk_clean.n, i_I=cdr_clk_clean.p, i_IB=cdr_clk_clean.n,
o_O=cdr_clk_clean_buf) o_O=cdr_clk_clean_buf,
p_CLKCM_CFG="TRUE",
p_CLKRCV_TRST="TRUE",
p_CLKSWING_CFG=3)
qpll_drtio_settings = QPLLSettings( qpll_drtio_settings = QPLLSettings(
refclksel=0b001, refclksel=0b001,
fbdiv=4, fbdiv=4,
@ -583,24 +596,7 @@ class SatelliteBase(BaseSoC):
rtio_clk_period = 1e9/rtio_clk_freq rtio_clk_period = 1e9/rtio_clk_freq
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6) self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
if with_wrpll:
self.submodules.wrpll_sampler = DDMTDSamplerGTP(
self.drtio_transceiver,
platform.request("cdr_clk_clean_fabric"))
helper_clk_pads = platform.request("ddmtd_helper_clk")
self.submodules.wrpll = WRPLL(
helper_clk_pads=helper_clk_pads,
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"),
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"),
ddmtd_inputs=self.wrpll_sampler)
self.csr_devices.append("wrpll")
# note: do not use self.wrpll.cd_helper.clk; otherwise, vivado craps out with:
# critical warning: create_clock attempting to set clock on an unknown port/pin
# command: "create_clock -period 7.920000 -waveform {0.000000 3.960000} -name
# helper_clk [get_xlnx_outside_genome_inst_pin 20 0]
platform.add_period_constraint(helper_clk_pads.p, rtio_clk_period*0.99)
platform.add_false_path_constraints(self.crg.cd_sys.clk, helper_clk_pads.p)
else:
self.submodules.siphaser = SiPhaser7Series( self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("cdr_clk") if platform.hw_rev == "v2.0" si5324_clkin=platform.request("cdr_clk") if platform.hw_rev == "v2.0"
else platform.request("si5324_clkin"), else platform.request("si5324_clkin"),
@ -619,9 +615,6 @@ class SatelliteBase(BaseSoC):
platform.add_false_path_constraints( platform.add_false_path_constraints(
self.crg.cd_sys.clk, self.crg.cd_sys.clk,
gtp.txoutclk, gtp.rxoutclk) gtp.txoutclk, gtp.rxoutclk)
if with_wrpll:
platform.add_false_path_constraints(
helper_clk_pads.p, gtp.rxoutclk)
for gtp in self.drtio_transceiver.gtps[1:]: for gtp in self.drtio_transceiver.gtps[1:]:
platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period) platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period)
platform.add_false_path_constraints( platform.add_false_path_constraints(
@ -700,14 +693,11 @@ def main():
parser.add_argument("-V", "--variant", default="tester", parser.add_argument("-V", "--variant", default="tester",
help="variant: {} (default: %(default)s)".format( help="variant: {} (default: %(default)s)".format(
"/".join(sorted(VARIANTS.keys())))) "/".join(sorted(VARIANTS.keys()))))
parser.add_argument("--with-wrpll", default=False, action="store_true")
parser.add_argument("--gateware-identifier-str", default=None, parser.add_argument("--gateware-identifier-str", default=None,
help="Override ROM identifier") help="Override ROM identifier")
args = parser.parse_args() args = parser.parse_args()
argdict = dict() argdict = dict()
if args.with_wrpll:
argdict["with_wrpll"] = True
argdict["gateware_identifier_str"] = args.gateware_identifier_str argdict["gateware_identifier_str"] = args.gateware_identifier_str
variant = args.variant.lower() variant = args.variant.lower()

View File

@ -46,6 +46,11 @@ class GenericStandalone(StandaloneBase):
phy = ttl_simple.Output(sfp_ctl.led) phy = ttl_simple.Output(sfp_ctl.led)
self.submodules += phy self.submodules += phy
self.rtio_channels.append(rtio.Channel.from_phy(phy)) self.rtio_channels.append(rtio.Channel.from_phy(phy))
if hw_rev == "v2.0":
for i in (1, 2):
phy = ttl_simple.Output(self.platform.request("user_led", i))
self.submodules += phy
self.rtio_channels.append(rtio.Channel.from_phy(phy))
self.config["HAS_RTIO_LOG"] = None self.config["HAS_RTIO_LOG"] = None
self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels) self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)

View File

@ -54,7 +54,7 @@ class Master(MiniSoC, AMPSoC):
add_identifier(self, gateware_identifier_str=gateware_identifier_str) add_identifier(self, gateware_identifier_str=gateware_identifier_str)
platform = self.platform platform = self.platform
rtio_clk_freq = 150e6 rtio_clk_freq = 125e6
self.comb += platform.request("input_clk_sel").eq(1) self.comb += platform.request("input_clk_sel").eq(1)
self.comb += platform.request("filtered_clk_sel").eq(1) self.comb += platform.request("filtered_clk_sel").eq(1)

View File

@ -21,7 +21,6 @@ from artiq.gateware import fmcdio_vhdci_eem
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_ultrascale, sawg from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_ultrascale, sawg
from artiq.gateware.drtio.transceiver import gth_ultrascale from artiq.gateware.drtio.transceiver import gth_ultrascale
from artiq.gateware.drtio.siphaser import SiPhaser7Series from artiq.gateware.drtio.siphaser import SiPhaser7Series
from artiq.gateware.drtio.wrpll import WRPLL, DDMTDSamplerExtFF
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
from artiq.gateware.drtio import * from artiq.gateware.drtio import *
from artiq.build_soc import * from artiq.build_soc import *
@ -54,7 +53,7 @@ class SatelliteBase(MiniSoC):
} }
mem_map.update(MiniSoC.mem_map) mem_map.update(MiniSoC.mem_map)
def __init__(self, rtio_clk_freq=125e6, identifier_suffix="", gateware_identifier_str=None, with_sfp=False, *, with_wrpll, **kwargs): def __init__(self, rtio_clk_freq=125e6, identifier_suffix="", gateware_identifier_str=None, with_sfp=False, **kwargs):
MiniSoC.__init__(self, MiniSoC.__init__(self,
cpu_type="or1k", cpu_type="or1k",
sdram_controller_type="minicon", sdram_controller_type="minicon",
@ -68,10 +67,6 @@ class SatelliteBase(MiniSoC):
platform = self.platform platform = self.platform
if with_wrpll:
clock_recout_pads = platform.request("ddmtd_rec_clk")
else:
clock_recout_pads = None
if with_sfp: if with_sfp:
# Use SFP0 to connect to master (Kasli) # Use SFP0 to connect to master (Kasli)
self.comb += platform.request("sfp_tx_disable", 0).eq(0) self.comb += platform.request("sfp_tx_disable", 0).eq(0)
@ -83,7 +78,7 @@ class SatelliteBase(MiniSoC):
data_pads=[drtio_uplink, platform.request("rtm_amc_link")], data_pads=[drtio_uplink, platform.request("rtm_amc_link")],
sys_clk_freq=self.clk_freq, sys_clk_freq=self.clk_freq,
rtio_clk_freq=rtio_clk_freq, rtio_clk_freq=rtio_clk_freq,
clock_recout_pads=clock_recout_pads) clock_recout_pads=None)
self.csr_devices.append("drtio_transceiver") self.csr_devices.append("drtio_transceiver")
self.submodules.rtio_tsc = rtio.TSC("sync", glbl_fine_ts_width=3) self.submodules.rtio_tsc = rtio.TSC("sync", glbl_fine_ts_width=3)
@ -133,23 +128,6 @@ class SatelliteBase(MiniSoC):
rtio_clk_period = 1e9/rtio_clk_freq rtio_clk_period = 1e9/rtio_clk_freq
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6) self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
if with_wrpll:
self.comb += [
platform.request("filtered_clk_sel").eq(0),
platform.request("ddmtd_main_dcxo_oe").eq(1),
platform.request("ddmtd_helper_dcxo_oe").eq(1)
]
self.submodules.wrpll_sampler = DDMTDSamplerExtFF(
platform.request("ddmtd_inputs"))
self.submodules.wrpll = WRPLL(
helper_clk_pads=platform.request("ddmtd_helper_clk"),
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"),
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"),
ddmtd_inputs=self.wrpll_sampler)
self.csr_devices.append("wrpll")
platform.add_period_constraint(self.wrpll.cd_helper.clk, rtio_clk_period*0.99)
platform.add_false_path_constraints(self.crg.cd_sys.clk, self.wrpll.cd_helper.clk)
else:
self.comb += platform.request("filtered_clk_sel").eq(1) self.comb += platform.request("filtered_clk_sel").eq(1)
self.submodules.siphaser = SiPhaser7Series( self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("si5324_clkin"), si5324_clkin=platform.request("si5324_clkin"),
@ -174,6 +152,22 @@ class SatelliteBase(MiniSoC):
self.crg.cd_sys.clk, self.crg.cd_sys.clk,
gth.txoutclk, gth.rxoutclk) gth.txoutclk, gth.rxoutclk)
# SSP1
# (Note: append() to csr_devices automatically adds "HAS_DEVICE_NAME_UPPERCASE"
# to Rust cfg, by misoc.integration.cpu_interface.get_rust_cfg().)
# MMC SPI GPIO input submodule
class Mmcspi(Module, AutoCSR):
def __init__(self, ssp):
self.submodules.cs_n = gpio.GPIOIn(ssp.cs_n)
self.submodules.clk = gpio.GPIOIn(ssp.clk)
self.submodules.mosi = gpio.GPIOIn(ssp.mosi)
# SPI GPIO core for bitbanging
ssp1 = self.platform.request("ssp1")
self.submodules.mmcspi = Mmcspi(ssp1)
self.csr_devices.append("mmcspi")
def add_rtio(self, rtio_channels): def add_rtio(self, rtio_channels):
# Only add MonInj core if there is anything to monitor # Only add MonInj core if there is anything to monitor
if any([len(c.probes) for c in rtio_channels]): if any([len(c.probes) for c in rtio_channels]):
@ -195,11 +189,11 @@ class SatelliteBase(MiniSoC):
class JDCGSAWG(Module, AutoCSR): class JDCGSAWG(Module, AutoCSR):
def __init__(self, platform, sys_crg, jesd_crg, dac): def __init__(self, platform, sys_crg, jesd_crg, dac):
# Kintex Ultrascale GTH, speed grade -1C: # Kintex Ultrascale GTH, speed grade -1C:
# CPLL linerate (D=1): 4.0 - 8.5 Gb/s # QPLL0 linerate (D=1): 9.8 - 12.5 Gb/s
self.submodules.jesd = jesd204_tools.UltrascaleTX( self.submodules.jesd = jesd204_tools.UltrascaleTX(
platform, sys_crg, jesd_crg, dac) platform, sys_crg, jesd_crg, dac, pll_type="qpll", tx_half=True)
self.submodules.sawgs = [sawg.Channel(width=16, parallelism=4) for i in range(4)] self.submodules.sawgs = [sawg.Channel(width=16, parallelism=8) for i in range(4)]
for conv, ch in zip(self.jesd.core.sink.flatten(), self.sawgs): for conv, ch in zip(self.jesd.core.sink.flatten(), self.sawgs):
assert len(Cat(ch.o)) == len(conv) assert len(Cat(ch.o)) == len(conv)
@ -209,34 +203,42 @@ class JDCGSAWG(Module, AutoCSR):
class JDCGPattern(Module, AutoCSR): class JDCGPattern(Module, AutoCSR):
def __init__(self, platform, sys_crg, jesd_crg, dac): def __init__(self, platform, sys_crg, jesd_crg, dac):
self.submodules.jesd = jesd204_tools.UltrascaleTX( self.submodules.jesd = jesd204_tools.UltrascaleTX(
platform, sys_crg, jesd_crg, dac) platform, sys_crg, jesd_crg, dac, pll_type="qpll", tx_half=True)
self.sawgs = [] self.sawgs = []
ramp = Signal(4) ramp = Signal(4)
self.sync.rtio += ramp.eq(ramp + 1) self.sync.rtio += ramp.eq(ramp + 1)
samples = [[Signal(16) for i in range(4)] for j in range(4)] samples = [[Signal(16) for i in range(8)] for j in range(4)]
self.comb += [ self.comb += [
a.eq(Cat(b)) for a, b in zip( a.eq(Cat(b)) for a, b in zip(
self.jesd.core.sink.flatten(), samples) self.jesd.core.sink.flatten(), samples)
] ]
# ch0: 16-step ramp with big carry toggles # ch0: 16-step ramp with big carry toggles
for i in range(4): for i in range(8):
self.comb += [ self.comb += [
samples[0][i][-4:].eq(ramp), samples[0][i][-4:].eq(ramp),
samples[0][i][:-4].eq(0x7ff if i % 2 else 0x800) samples[0][i][:-4].eq(0x7ff if i % 2 else 0x800)
] ]
# ch1: 50 MHz # ch1: 50 MHz
# - Formulae:
# target cosine wave frequency: f = 50e6
# DAC sampling frequency: fs = 1000e6
# number of samples per coarse RTIO period: P = 8
# number of samples needed per wave period: M = (1/f) / (1/fs)) = 20
# number of repeating samples needed: N = LCM(P, M) = 40
# number of RTIO periods needed for repeating: k = N/P = 5
# discretized value of the wave: y[i] = cos(i/M * 2pi)
from math import pi, cos from math import pi, cos
data = [int(round(cos(i/12*2*pi)*((1 << 15) - 1))) data = [int(round(cos(i/20*2*pi)*((1 << 15) - 1)))
for i in range(12)] for i in range(40)]
k = Signal(2) k = Signal(max=5)
self.sync.rtio += If(k == 2, k.eq(0)).Else(k.eq(k + 1)) self.sync.rtio += If(k == 4, k.eq(0)).Else(k.eq(k + 1))
self.comb += [ self.comb += [
Case(k, { Case(k, {
i: [samples[1][j].eq(data[i*4 + j]) for j in range(4)] i: [samples[1][j].eq(data[i*8 + j]) for j in range(8)]
for i in range(3) for i in range(5)
}) })
] ]
# ch2: ch0, ch3: ch1 # ch2: ch0, ch3: ch1
@ -249,13 +251,13 @@ class JDCGPattern(Module, AutoCSR):
class JDCGSyncDDS(Module, AutoCSR): class JDCGSyncDDS(Module, AutoCSR):
def __init__(self, platform, sys_crg, jesd_crg, dac): def __init__(self, platform, sys_crg, jesd_crg, dac):
self.submodules.jesd = jesd204_tools.UltrascaleTX( self.submodules.jesd = jesd204_tools.UltrascaleTX(
platform, sys_crg, jesd_crg, dac) platform, sys_crg, jesd_crg, dac, pll_type="qpll", tx_half=True)
self.coarse_ts = Signal(32) self.coarse_ts = Signal(32)
self.sawgs = [] self.sawgs = []
ftw = round(2**len(self.coarse_ts)*9e6/600e6) ftw = round(2**len(self.coarse_ts)*9e6/1000e6)
parallelism = 4 parallelism = 8
mul_1 = Signal.like(self.coarse_ts) mul_1 = Signal.like(self.coarse_ts)
mul_2 = Signal.like(self.coarse_ts) mul_2 = Signal.like(self.coarse_ts)
@ -292,10 +294,8 @@ class Satellite(SatelliteBase):
""" """
DRTIO satellite with local DAC/SAWG channels, as well as TTL channels via FMC and VHDCI carrier. DRTIO satellite with local DAC/SAWG channels, as well as TTL channels via FMC and VHDCI carrier.
""" """
def __init__(self, jdcg_type, **kwargs): def __init__(self, jdcg_type, ttlout=False, mcx_sqwave=False, **kwargs):
SatelliteBase.__init__(self, 150e6, SatelliteBase.__init__(self, identifier_suffix="." + jdcg_type, **kwargs)
identifier_suffix="." + jdcg_type,
**kwargs)
platform = self.platform platform = self.platform
@ -318,19 +318,24 @@ class Satellite(SatelliteBase):
phy = ttl_simple.Output(platform.request("user_led", i)) phy = ttl_simple.Output(platform.request("user_led", i))
self.submodules += phy self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy)) rtio_channels.append(rtio.Channel.from_phy(phy))
mcx_io = platform.request("mcx_io", 0) for i in range(2):
phy = ttl_serdes_ultrascale.InOut(4, mcx_io.level) mcx_io = platform.request("mcx_io", i)
self.comb += mcx_io.direction.eq(phy.oe) if mcx_sqwave:
self.submodules += phy phy = ttl_serdes_ultrascale.CustomOutput(4, mcx_io.level,
rtio_channels.append(rtio.Channel.from_phy(phy)) driver_type="square_wave",
mcx_io = platform.request("mcx_io", 1) rtio_freq=self.rtio_clk_freq,
wave_freq=9e6)
self.comb += mcx_io.direction.eq(1)
elif ttlout:
phy = ttl_serdes_ultrascale.Output(4, mcx_io.level)
self.comb += mcx_io.direction.eq(1)
else:
phy = ttl_serdes_ultrascale.InOut(4, mcx_io.level) phy = ttl_serdes_ultrascale.InOut(4, mcx_io.level)
self.comb += mcx_io.direction.eq(phy.oe) self.comb += mcx_io.direction.eq(phy.oe)
self.submodules += phy self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy)) rtio_channels.append(rtio.Channel.from_phy(phy))
self.submodules.jesd_crg = jesd204_tools.UltrascaleCRG( self.submodules.jesd_crg = jesd204_tools.UltrascaleCRG(platform)
platform, use_rtio_clock=True)
cls = { cls = {
"sawg": JDCGSAWG, "sawg": JDCGSAWG,
"pattern": JDCGPattern, "pattern": JDCGPattern,
@ -432,7 +437,10 @@ def main():
default="sawg", default="sawg",
help="Change type of signal generator. This is used exclusively for " help="Change type of signal generator. This is used exclusively for "
"development and debugging.") "development and debugging.")
parser.add_argument("--with-wrpll", default=False, action="store_true") parser.add_argument("--ttlout", default=False, action="store_true",
help="force only outputs on the MCX TTL IOs")
parser.add_argument("--mcx-sqwave", default=False, action="store_true",
help="generate a square wave on the MCX TTL IOs")
parser.add_argument("--gateware-identifier-str", default=None, parser.add_argument("--gateware-identifier-str", default=None,
help="Override ROM identifier") help="Override ROM identifier")
args = parser.parse_args() args = parser.parse_args()
@ -442,13 +450,13 @@ def main():
soc = Satellite( soc = Satellite(
with_sfp=args.sfp, with_sfp=args.sfp,
jdcg_type=args.jdcg_type, jdcg_type=args.jdcg_type,
with_wrpll=args.with_wrpll, ttlout=args.ttlout,
mcx_sqwave=args.mcx_sqwave,
gateware_identifier_str=args.gateware_identifier_str, gateware_identifier_str=args.gateware_identifier_str,
**soc_sayma_amc_argdict(args)) **soc_sayma_amc_argdict(args))
elif variant == "simplesatellite": elif variant == "simplesatellite":
soc = SimpleSatellite( soc = SimpleSatellite(
with_sfp=args.sfp, with_sfp=args.sfp,
with_wrpll=args.with_wrpll,
gateware_identifier_str=args.gateware_identifier_str, gateware_identifier_str=args.gateware_identifier_str,
**soc_sayma_amc_argdict(args)) **soc_sayma_amc_argdict(args))
else: else:

View File

@ -20,7 +20,6 @@ from artiq.gateware import jesd204_tools
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series
from artiq.gateware.drtio.transceiver import gtp_7series from artiq.gateware.drtio.transceiver import gtp_7series
from artiq.gateware.drtio.siphaser import SiPhaser7Series from artiq.gateware.drtio.siphaser import SiPhaser7Series
from artiq.gateware.drtio.wrpll import WRPLL, DDMTDSamplerGTP
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
from artiq.gateware.drtio import * from artiq.gateware.drtio import *
from artiq.build_soc import add_identifier from artiq.build_soc import add_identifier
@ -75,7 +74,7 @@ class _SatelliteBase(BaseSoC):
} }
mem_map.update(BaseSoC.mem_map) mem_map.update(BaseSoC.mem_map)
def __init__(self, rtio_clk_freq, *, with_wrpll, gateware_identifier_str, **kwargs): def __init__(self, rtio_clk_freq, *, gateware_identifier_str, **kwargs):
BaseSoC.__init__(self, BaseSoC.__init__(self,
cpu_type="or1k", cpu_type="or1k",
**kwargs) **kwargs)
@ -91,7 +90,10 @@ class _SatelliteBase(BaseSoC):
self.specials += Instance("IBUFDS_GTE2", self.specials += Instance("IBUFDS_GTE2",
i_CEB=disable_cdrclkc_ibuf, i_CEB=disable_cdrclkc_ibuf,
i_I=cdrclkc_clkout.p, i_IB=cdrclkc_clkout.n, i_I=cdrclkc_clkout.p, i_IB=cdrclkc_clkout.n,
o_O=cdrclkc_clkout_buf) o_O=cdrclkc_clkout_buf,
p_CLKCM_CFG="TRUE",
p_CLKRCV_TRST="TRUE",
p_CLKSWING_CFG=3)
qpll_drtio_settings = QPLLSettings( qpll_drtio_settings = QPLLSettings(
refclksel=0b001, refclksel=0b001,
fbdiv=4, fbdiv=4,
@ -136,25 +138,7 @@ class _SatelliteBase(BaseSoC):
gtp = self.drtio_transceiver.gtps[0] gtp = self.drtio_transceiver.gtps[0]
rtio_clk_period = 1e9/rtio_clk_freq rtio_clk_period = 1e9/rtio_clk_freq
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6) self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
if with_wrpll:
self.comb += [
platform.request("filtered_clk_sel").eq(0),
platform.request("ddmtd_main_dcxo_oe").eq(1),
platform.request("ddmtd_helper_dcxo_oe").eq(1)
]
self.submodules.wrpll_sampler = DDMTDSamplerGTP(
self.drtio_transceiver,
platform.request("cdr_clk_clean_fabric"))
self.submodules.wrpll = WRPLL(
helper_clk_pads=platform.request("ddmtd_helper_clk"),
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"),
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"),
ddmtd_inputs=self.wrpll_sampler)
self.csr_devices.append("wrpll")
platform.add_period_constraint(self.wrpll.cd_helper.clk, rtio_clk_period*0.99)
platform.add_false_path_constraints(self.crg.cd_sys.clk, self.wrpll.cd_helper.clk)
platform.add_false_path_constraints(self.wrpll.cd_helper.clk, gtp.rxoutclk)
else:
self.comb += platform.request("filtered_clk_sel").eq(1) self.comb += platform.request("filtered_clk_sel").eq(1)
self.submodules.siphaser = SiPhaser7Series( self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("si5324_clkin"), si5324_clkin=platform.request("si5324_clkin"),
@ -297,8 +281,7 @@ def main():
builder_args(parser) builder_args(parser)
soc_sayma_rtm_args(parser) soc_sayma_rtm_args(parser)
parser.add_argument("--rtio-clk-freq", parser.add_argument("--rtio-clk-freq",
default=150, type=int, help="RTIO clock frequency in MHz") default=125, type=int, help="RTIO clock frequency in MHz")
parser.add_argument("--with-wrpll", default=False, action="store_true")
parser.add_argument("--gateware-identifier-str", default=None, parser.add_argument("--gateware-identifier-str", default=None,
help="Override ROM identifier") help="Override ROM identifier")
parser.set_defaults(output_dir=os.path.join("artiq_sayma", "rtm")) parser.set_defaults(output_dir=os.path.join("artiq_sayma", "rtm"))
@ -306,7 +289,6 @@ def main():
soc = Satellite( soc = Satellite(
rtio_clk_freq=1e6*args.rtio_clk_freq, rtio_clk_freq=1e6*args.rtio_clk_freq,
with_wrpll=args.with_wrpll,
gateware_identifier_str=args.gateware_identifier_str, gateware_identifier_str=args.gateware_identifier_str,
**soc_sayma_rtm_argdict(args)) **soc_sayma_rtm_argdict(args))
builder = SatmanSoCBuilder(soc, **builder_argdict(args)) builder = SatmanSoCBuilder(soc, **builder_argdict(args))

View File

@ -67,6 +67,7 @@ def do_dma(dut, address):
test_writes1 = [ test_writes1 = [
(0x01, 0x23, 0x12, 0x33), (0x01, 0x23, 0x12, 0x33),
(0x901, 0x902, 0x11, 0xeeeeeeeeeeeeeefffffffffffffffffffffffffffffff28888177772736646717738388488), (0x901, 0x902, 0x11, 0xeeeeeeeeeeeeeefffffffffffffffffffffffffffffff28888177772736646717738388488),
(0x82, 0x289, 0x99, int.from_bytes(b"\xf0" * 64, "little")),
(0x81, 0x288, 0x88, 0x8888) (0x81, 0x288, 0x88, 0x8888)
] ]

View File

@ -43,7 +43,7 @@ class TB(Module):
) )
] ]
cnv_old = Signal(reset_less=True) cnv_old = Signal(reset_less=True)
self.sync.async += [ self.sync.async_ += [
cnv_old.eq(self.cnv), cnv_old.eq(self.cnv),
If(Cat(cnv_old, self.cnv) == 0b10, If(Cat(cnv_old, self.cnv) == 0b10,
sr.eq(Cat(reversed(self.data[2*i:2*i + 2]))), sr.eq(Cat(reversed(self.data[2*i:2*i + 2]))),
@ -62,7 +62,7 @@ class TB(Module):
def _dly(self, sig, n=0): def _dly(self, sig, n=0):
n += self.params.t_rtt*4//2 # t_{sys,adc,ret}/t_async half rtt n += self.params.t_rtt*4//2 # t_{sys,adc,ret}/t_async half rtt
dly = Signal(n, reset_less=True) dly = Signal(n, reset_less=True)
self.sync.async += dly.eq(Cat(sig, dly)) self.sync.async_ += dly.eq(Cat(sig, dly))
return dly[-1] return dly[-1]
@ -85,8 +85,8 @@ def main():
assert not (yield dut.done) assert not (yield dut.done)
while not (yield dut.done): while not (yield dut.done):
yield yield
x = (yield from [(yield d) for d in dut.data]) for i, d in enumerate(dut.data):
for i, ch in enumerate(x): ch = yield d
assert ch == i, (hex(ch), hex(i)) assert ch == i, (hex(ch), hex(i))
run_simulation(tb, [run(tb)], run_simulation(tb, [run(tb)],
@ -95,7 +95,7 @@ def main():
"sys": (8, 0), "sys": (8, 0),
"adc": (8, 0), "adc": (8, 0),
"ret": (8, 0), "ret": (8, 0),
"async": (2, 0), "async_": (2, 0),
}, },
) )

View File

@ -44,8 +44,10 @@ class TB(Module):
yield yield
dat = [] dat = []
for dds in self.ddss: for dds in self.ddss:
v = yield from [(yield getattr(dds, k)) v = []
for k in "cmd ftw pow asf".split()] for k in "cmd ftw pow asf".split():
f = yield getattr(dds, k)
v.append(f)
dat.append(v) dat.append(v)
data.append((i, dat)) data.append((i, dat))
else: else:

View File

@ -91,7 +91,7 @@ def main():
"sys": (8, 0), "sys": (8, 0),
"adc": (8, 0), "adc": (8, 0),
"ret": (8, 0), "ret": (8, 0),
"async": (2, 0), "async_": (2, 0),
}) })

View File

@ -1,158 +0,0 @@
import unittest
import numpy as np
from migen import *
from artiq.gateware.drtio.wrpll.ddmtd import Collector
from artiq.gateware.drtio.wrpll import thls, filters
class HelperChainTB(Module):
def __init__(self, N):
self.tag_ref = Signal(N)
self.input_stb = Signal()
self.adpll = Signal((24, True))
self.out_stb = Signal()
###
self.submodules.collector = Collector(N)
self.submodules.loop_filter = thls.make(filters.helper, data_width=48)
self.comb += [
self.collector.tag_ref.eq(self.tag_ref),
self.collector.ref_stb.eq(self.input_stb),
self.collector.main_stb.eq(self.input_stb),
self.loop_filter.input.eq(self.collector.out_helper << 22),
self.loop_filter.input_stb.eq(self.collector.out_stb),
self.adpll.eq(self.loop_filter.output),
self.out_stb.eq(self.loop_filter.output_stb),
]
class TestDSP(unittest.TestCase):
def test_main_collector(self):
N = 2
collector = Collector(N=N)
# check collector phase unwrapping
tags = [(0, 0, 0),
(0, 1, 1),
(2, 1, -1),
(3, 1, -2),
(0, 1, -3),
(1, 1, -4),
(2, 1, -5),
(3, 1, -6),
(3, 3, -4),
(0, 0, -4),
(0, 1, -3),
(0, 2, -2),
(0, 3, -1),
(0, 0, 0)]
for i in range(10):
tags.append((i % (2**N), (i+1) % (2**N), 1))
def generator():
for tag_ref, tag_main, out in tags:
yield collector.tag_ref.eq(tag_ref)
yield collector.tag_main.eq(tag_main)
yield collector.main_stb.eq(1)
yield collector.ref_stb.eq(1)
yield
yield collector.main_stb.eq(0)
yield collector.ref_stb.eq(0)
while not (yield collector.out_stb):
yield
out_main = yield collector.out_main
self.assertEqual(out_main, out)
run_simulation(collector, generator())
def test_helper_collector(self):
N = 3
collector = Collector(N=N)
# check collector phase unwrapping
tags = [((2**N - 1 - tag) % (2**N), -1) for tag in range(20)]
tags += [((tags[-1][0] + 1 + tag) % (2**N), 1) for tag in range(20)]
tags += [((tags[-1][0] - 2 - 2*tag) % (2**N), -2) for tag in range(20)]
def generator():
for tag_ref, out in tags:
yield collector.tag_ref.eq(tag_ref)
yield collector.main_stb.eq(1)
yield collector.ref_stb.eq(1)
yield
yield collector.main_stb.eq(0)
yield collector.ref_stb.eq(0)
while not (yield collector.out_stb):
yield
out_helper = yield collector.out_helper
self.assertEqual(out_helper, out)
run_simulation(collector, generator())
# test helper collector + filter against output from MATLAB model
def test_helper_chain(self):
pll = HelperChainTB(15)
initial_helper_out = -8000
ref_tags = np.array([
24778, 16789, 8801, 814, 25596, 17612, 9628, 1646,
26433, 18453, 10474, 2496, 27287, 19311, 11337, 3364, 28160,
20190, 12221, 4253, 29054, 21088, 13124, 5161, 29966, 22005,
14045, 6087, 30897, 22940, 14985, 7031, 31847, 23895, 15944,
7995, 47, 24869, 16923, 8978, 1035, 25861, 17920, 9981,
2042, 26873, 18937, 11002, 3069, 27904, 19973, 12042, 4113,
28953, 21026, 13100, 5175, 30020, 22098, 14177, 6257, 31106,
23189, 15273, 7358, 32212, 24300, 16388, 8478, 569, 25429,
17522, 9617, 1712, 26577, 18675, 10774, 2875, 27745, 19848,
11951, 4056, 28930, 21038, 13147, 5256, 30135, 22247, 14361,
6475, 31359, 23476, 15595, 7714, 32603, 24725, 16847, 8971,
1096
])
adpll_sim = np.array([
8, 24, 41, 57, 74, 91, 107, 124, 140, 157, 173,
190, 206, 223, 239, 256, 273, 289, 306, 322, 339, 355,
372, 388, 405, 421, 438, 454, 471, 487, 504, 520, 537,
553, 570, 586, 603, 619, 636, 652, 668, 685, 701, 718,
734, 751, 767, 784, 800, 817, 833, 850, 866, 882, 899,
915, 932, 948, 965, 981, 998, 1014, 1030, 1047, 1063, 1080,
1096, 1112, 1129, 1145, 1162, 1178, 1194, 1211, 1227, 1244, 1260,
1276, 1293, 1309, 1326, 1342, 1358, 1375, 1391, 1407, 1424, 1440,
1457, 1473, 1489, 1506, 1522, 1538, 1555, 1571, 1587, 1604, 1620,
1636])
def sim():
yield pll.collector.out_helper.eq(initial_helper_out)
for ref_tag, adpll_matlab in zip(ref_tags, adpll_sim):
# feed collector
yield pll.tag_ref.eq(int(ref_tag))
yield pll.input_stb.eq(1)
yield
yield pll.input_stb.eq(0)
while not (yield pll.collector.out_stb):
yield
tag_diff = yield pll.collector.out_helper
while not (yield pll.loop_filter.output_stb):
yield
adpll_migen = yield pll.adpll
self.assertEqual(adpll_migen, adpll_matlab)
yield
run_simulation(pll, [sim()])

View File

@ -1,55 +0,0 @@
import unittest
from migen import *
from artiq.gateware.drtio.wrpll import thls
a = 0
def simple_test(x):
global a
a = a + (x*4 >> 1)
return a
class TestTHLS(unittest.TestCase):
def test_thls(self):
global a
proc = thls.Processor()
a = 0
cp = thls.compile(proc, simple_test)
print("Program:")
cp.pretty_print()
cp.dimension_processor()
print("Encoded program:", cp.encode())
proc_impl = proc.implement(cp.encode(), cp.data)
def send_values(values):
for value in values:
yield proc_impl.input.eq(value)
yield proc_impl.input_stb.eq(1)
yield
yield proc_impl.input.eq(0)
yield proc_impl.input_stb.eq(0)
yield
while (yield proc_impl.busy):
yield
@passive
def receive_values(callback):
while True:
while not (yield proc_impl.output_stb):
yield
callback((yield proc_impl.output))
yield
send_list = [42, 40, 10, 10]
receive_list = []
run_simulation(proc_impl, [send_values(send_list), receive_values(receive_list.append)])
print("Execution:", send_list, "->", receive_list)
a = 0
expected_list = [simple_test(x) for x in send_list]
self.assertEqual(receive_list, expected_list)

View File

@ -100,6 +100,17 @@ class NumberEntryInt(QtWidgets.QSpinBox):
if "default" in procdesc: if "default" in procdesc:
return procdesc["default"] return procdesc["default"]
else: else:
have_max = "max" in procdesc and procdesc["max"] is not None
have_min = "min" in procdesc and procdesc["min"] is not None
if have_max and have_min:
if procdesc["min"] <= 0 < procdesc["max"]:
return 0
elif have_min and not have_max:
if procdesc["min"] >= 0:
return procdesc["min"]
elif not have_min and have_max:
if procdesc["max"] < 0:
return procdesc["max"]
return 0 return 0
@ -416,9 +427,10 @@ class ScanEntry(LayoutWidget):
"selected": "NoScan", "selected": "NoScan",
"NoScan": {"value": 0.0, "repetitions": 1}, "NoScan": {"value": 0.0, "repetitions": 1},
"RangeScan": {"start": 0.0, "stop": 100.0*scale, "npoints": 10, "RangeScan": {"start": 0.0, "stop": 100.0*scale, "npoints": 10,
"randomize": False}, "randomize": False, "seed": None},
"CenterScan": {"center": 0.*scale, "span": 100.*scale, "CenterScan": {"center": 0.*scale, "span": 100.*scale,
"step": 10.*scale, "randomize": False}, "step": 10.*scale, "randomize": False,
"seed": None},
"ExplicitScan": {"sequence": []} "ExplicitScan": {"sequence": []}
} }
if "default" in procdesc: if "default" in procdesc:

View File

@ -8,7 +8,7 @@ from artiq.language import units
from artiq.language.core import rpc from artiq.language.core import rpc
__all__ = ["NoDefault", __all__ = ["NoDefault", "DefaultMissing",
"PYONValue", "BooleanValue", "EnumerationValue", "PYONValue", "BooleanValue", "EnumerationValue",
"NumberValue", "StringValue", "NumberValue", "StringValue",
"HasEnvironment", "Experiment", "EnvExperiment"] "HasEnvironment", "Experiment", "EnvExperiment"]
@ -491,7 +491,7 @@ def is_experiment(o):
def is_public_experiment(o): def is_public_experiment(o):
"""Checks if a Pyhton object is a top-level, """Checks if a Python object is a top-level,
non underscore-prefixed, experiment class. non underscore-prefixed, experiment class.
""" """
return is_experiment(o) and not o.__name__.startswith("_") return is_experiment(o) and not o.__name__.startswith("_")

View File

@ -307,9 +307,9 @@ def main():
elif action == "analyze": elif action == "analyze":
try: try:
exp_inst.analyze() exp_inst.analyze()
put_completed()
finally: finally:
write_results() write_results()
put_completed()
elif action == "examine": elif action == "examine":
examine(ExamineDeviceMgr, ExamineDatasetMgr, obj["file"]) examine(ExamineDeviceMgr, ExamineDatasetMgr, obj["file"])
put_completed() put_completed()

View File

@ -152,7 +152,12 @@ class SSHClient(Client):
if get_pty: if get_pty:
chan.get_pty() chan.get_pty()
cmd = " ".join([shlex.quote(arg.format(tmp=self._tmpr, **kws)) for arg in cmd]) cmd = " ".join([shlex.quote(arg.format(tmp=self._tmpr, **kws)) for arg in cmd])
logger.debug("Executing {}".format(cmd))
# Wrap command in a bash login shell
cmd = "exec {}".format(cmd)
cmd = "bash --login -c {}".format(shlex.quote(cmd))
logger.debug("Executing: {}".format(cmd))
chan.exec_command(cmd) chan.exec_command(cmd)
return chan return chan

View File

@ -36,6 +36,12 @@ class RoundtripTest(ExperimentCase):
self.assertRoundtrip(True) self.assertRoundtrip(True)
self.assertRoundtrip(False) self.assertRoundtrip(False)
def test_numpy_bool(self):
# These won't return as numpy.bool_, but the bare-Python results should still
# compare equal.
self.assertRoundtrip(numpy.True_)
self.assertRoundtrip(numpy.False_)
def test_int(self): def test_int(self):
self.assertRoundtrip(numpy.int32(42)) self.assertRoundtrip(numpy.int32(42))
self.assertRoundtrip(numpy.int64(42)) self.assertRoundtrip(numpy.int64(42))
@ -55,6 +61,12 @@ class RoundtripTest(ExperimentCase):
def test_list(self): def test_list(self):
self.assertRoundtrip([10]) self.assertRoundtrip([10])
def test_bool_list(self):
self.assertRoundtrip([True, False])
def test_int64_list(self):
self.assertRoundtrip([numpy.int64(0), numpy.int64(1)])
def test_object(self): def test_object(self):
obj = object() obj = object()
self.assertRoundtrip(obj) self.assertRoundtrip(obj)
@ -69,6 +81,7 @@ class RoundtripTest(ExperimentCase):
self.assertRoundtrip([(0x12345678, [("foo", [0.0, 1.0], [0, 1])])]) self.assertRoundtrip([(0x12345678, [("foo", [0.0, 1.0], [0, 1])])])
def test_array_1d(self): def test_array_1d(self):
self.assertArrayRoundtrip(numpy.array([True, False]))
self.assertArrayRoundtrip(numpy.array([1, 2, 3], dtype=numpy.int32)) self.assertArrayRoundtrip(numpy.array([1, 2, 3], dtype=numpy.int32))
self.assertArrayRoundtrip(numpy.array([1.0, 2.0, 3.0])) self.assertArrayRoundtrip(numpy.array([1.0, 2.0, 3.0]))
self.assertArrayRoundtrip(numpy.array(["a", "b", "c"])) self.assertArrayRoundtrip(numpy.array(["a", "b", "c"]))
@ -206,6 +219,7 @@ class RPCTypesTest(ExperimentCase):
class _RPCCalls(EnvExperiment): class _RPCCalls(EnvExperiment):
def build(self): def build(self):
self.setattr_device("core") self.setattr_device("core")
self._list_int64 = [numpy.int64(1)]
def args(self, *args) -> TInt32: def args(self, *args) -> TInt32:
return len(args) return len(args)
@ -241,6 +255,10 @@ class _RPCCalls(EnvExperiment):
def args1kwargs2(self): def args1kwargs2(self):
return self.kwargs("X", a="A", b=1) return self.kwargs("X", a="A", b=1)
@kernel
def list_int64(self):
return self._list_int64
@kernel @kernel
def numpy_things(self): def numpy_things(self):
return (numpy.int32(10), numpy.int64(20), numpy.array([42,])) return (numpy.int32(10), numpy.int64(20), numpy.array([42,]))
@ -285,6 +303,10 @@ class RPCCallsTest(ExperimentCase):
self.assertEqual(exp.args1kwargs2(), 2) self.assertEqual(exp.args1kwargs2(), 2)
self.assertEqual(exp.numpy_things(), self.assertEqual(exp.numpy_things(),
(numpy.int32(10), numpy.int64(20), numpy.array([42,]))) (numpy.int32(10), numpy.int64(20), numpy.array([42,])))
# Ensure lists of int64s don't decay to variable-length builtin integers.
list_int64 = exp.list_int64()
self.assertEqual(list_int64, [numpy.int64(1)])
self.assertTrue(isinstance(list_int64[0], numpy.int64))
self.assertTrue((exp.numpy_full() == numpy.full(10, 20)).all()) self.assertTrue((exp.numpy_full() == numpy.full(10, 20)).all())
self.assertTrue((exp.numpy_full_matrix() == numpy.full((3, 2), 13)).all()) self.assertTrue((exp.numpy_full_matrix() == numpy.full((3, 2), 13)).all())
self.assertTrue(numpy.isnan(exp.numpy_nan()).all()) self.assertTrue(numpy.isnan(exp.numpy_nan()).all())
@ -488,3 +510,23 @@ class AssertTest(ExperimentCase):
check_fail(lambda: exp.check(False), "AssertionError") check_fail(lambda: exp.check(False), "AssertionError")
exp.check_msg(True) exp.check_msg(True)
check_fail(lambda: exp.check_msg(False), "foo") check_fail(lambda: exp.check_msg(False), "foo")
class _NumpyBool(EnvExperiment):
def build(self):
self.setattr_device("core")
self.np_true = numpy.True_
self.np_false = numpy.False_
@kernel
def run(self):
assert self.np_true
assert self.np_true == True
assert not self.np_false
assert self.np_false == False
class NumpyBoolTest(ExperimentCase):
def test_numpy_bool(self):
"""Test NumPy bools decay to ARTIQ compiler builtin bools as expected"""
self.create(_NumpyBool).run()

View File

@ -72,13 +72,13 @@ class CompareHostDeviceTest(ExperimentCase):
# randomised tests instead. # randomised tests instead.
# TODO: Provoke overflows, division by zero, etc., and compare results. # TODO: Provoke overflows, division by zero, etc., and compare results.
args = [(typ(a), typ(b)) for a, b in [(0, 1), (3, 2), (11, 6)] args = [(typ(a), typ(b)) for a, b in [(0, 1), (3, 2), (11, 6)]
for typ in [numpy.int32, numpy.int64, numpy.float]] for typ in [numpy.int32, numpy.int64, numpy.float64]]
for op in ELEM_WISE_BINOPS: for op in ELEM_WISE_BINOPS:
for arg in args: for arg in args:
self._test_binop("a" + op + "b", *arg) self._test_binop("a" + op + "b", *arg)
def test_scalar_matrix_binops(self): def test_scalar_matrix_binops(self):
for typ in [numpy.int32, numpy.int64, numpy.float]: for typ in [numpy.int32, numpy.int64, numpy.float64]:
scalar = typ(3) scalar = typ(3)
matrix = numpy.array([[4, 5, 6], [7, 8, 9]], dtype=typ) matrix = numpy.array([[4, 5, 6], [7, 8, 9]], dtype=typ)
for op in ELEM_WISE_BINOPS: for op in ELEM_WISE_BINOPS:
@ -88,7 +88,7 @@ class CompareHostDeviceTest(ExperimentCase):
self._test_binop(code, matrix, matrix) self._test_binop(code, matrix, matrix)
def test_matrix_mult(self): def test_matrix_mult(self):
for typ in [numpy.int32, numpy.int64, numpy.float]: for typ in [numpy.int32, numpy.int64, numpy.float64]:
mat_a = numpy.array([[1, 2, 3], [4, 5, 6]], dtype=typ) mat_a = numpy.array([[1, 2, 3], [4, 5, 6]], dtype=typ)
mat_b = numpy.array([[7, 8], [9, 10], [11, 12]], dtype=typ) mat_b = numpy.array([[7, 8], [9, 10], [11, 12]], dtype=typ)
self._test_binop("a @ b", mat_a, mat_b) self._test_binop("a @ b", mat_a, mat_b)
@ -139,7 +139,7 @@ class _MatrixMult(EnvExperiment):
def build(self): def build(self):
self.setattr_device("core") self.setattr_device("core")
self.imat = numpy.arange(4, dtype=numpy.int64).reshape((2, 2)) self.imat = numpy.arange(4, dtype=numpy.int64).reshape((2, 2))
self.fmat = numpy.arange(4, dtype=numpy.float).reshape((2, 2)) self.fmat = numpy.arange(4, dtype=numpy.float64).reshape((2, 2))
@kernel @kernel
def run(self): def run(self):

View File

@ -0,0 +1,51 @@
# RUN: %python -m artiq.compiler.testbench.embedding %s
from artiq.language.core import *
class InnerA:
def __init__(self, val):
self.val = val
@kernel
def run_once(self):
return self.val
class InnerB:
def __init__(self, val):
self.val = val
@kernel
def run_once(self):
return self.val
def make_runner(InnerCls, val):
class Runner:
def __init__(self):
self.inner = InnerCls(val)
@kernel
def run_once(self):
return self.inner.run_once()
return Runner()
class Parent:
def __init__(self):
self.a = make_runner(InnerA, 1)
self.b = make_runner(InnerB, 42.0)
@kernel
def run_once(self):
return self.a.run_once() + self.b.run_once()
parent = Parent()
@kernel
def entrypoint():
parent.run_once()

View File

@ -0,0 +1,16 @@
# RUN: %python -m artiq.compiler.testbench.embedding %s
from artiq.language.core import *
from artiq.language.types import *
@kernel
def consume_tuple(x: TTuple([TInt32, TBool])):
print(x)
@kernel
def return_tuple() -> TTuple([TInt32, TBool]):
return (123, False)
@kernel
def entrypoint():
consume_tuple(return_tuple())

View File

@ -1,10 +1,10 @@
# RUN: %python -m artiq.compiler.testbench.inferencer %s >%t # RUN: %python -m artiq.compiler.testbench.inferencer %s >%t
# RUN: OutputCheck %s --file-to-check=%t # RUN: OutputCheck %s --file-to-check=%t
# Nothing known, as there could be several more dimensions # CHECK-L: numpy.array(elt='a, num_dims=1)
# hidden from view by the array being empty.
# CHECK-L: ([]:list(elt='a)):'b
array([]) array([])
# CHECK-L: numpy.array(elt='b, num_dims=2)
array([[], []])
# CHECK-L: numpy.array(elt=numpy.int?, num_dims=1) # CHECK-L: numpy.array(elt=numpy.int?, num_dims=1)
array([1, 2, 3]) array([1, 2, 3])

View File

@ -9,5 +9,8 @@ b = array([1, 2, 3])
# CHECK-L: ${LINE:+1}: error: too many indices for array of dimension 1 # CHECK-L: ${LINE:+1}: error: too many indices for array of dimension 1
b[1, 2] b[1, 2]
# CHECK-L: ${LINE:+1}: error: strided slicing not yet supported for NumPy arrays
b[::-1]
# CHECK-L: ${LINE:+1}: error: array attributes cannot be assigned to # CHECK-L: ${LINE:+1}: error: array attributes cannot be assigned to
b.shape = (5, ) b.shape = (5, )

View File

@ -0,0 +1,6 @@
# RUN: %python -m artiq.compiler.testbench.inferencer %s >%t
# RUN: OutputCheck %s --file-to-check=%t
# CHECK-L: def foo(val:bool)->numpy.int?:
def foo(val):
return 1 if val else 0

View File

@ -33,8 +33,8 @@ j = []
j += [1.0] j += [1.0]
# CHECK-L: j:list(elt=float) # CHECK-L: j:list(elt=float)
1 if a else 2 1 if c else 2
# CHECK-L: 1:numpy.int? if a:numpy.int? else 2:numpy.int?:numpy.int? # CHECK-L: 1:numpy.int? if c:bool else 2:numpy.int?:numpy.int?
True and False True and False
# CHECK-L: True:bool and False:bool:bool # CHECK-L: True:bool and False:bool:bool

View File

@ -0,0 +1,24 @@
# RUN: %python -m artiq.compiler.testbench.jit %s
a = array([0, 1, 2, 3])
b = a[2:3]
assert b.shape == (1,)
assert b[0] == 2
b[0] = 5
assert a[2] == 5
b = a[3:2]
assert b.shape == (0,)
c = array([[0, 1], [2, 3]])
d = c[:1]
assert d.shape == (1, 2)
assert d[0, 0] == 0
assert d[0, 1] == 1
d[0, 0] = 5
assert c[0, 0] == 5
d = c[1:0]
assert d.shape == (0, 2)

View File

@ -0,0 +1,44 @@
# RUN: %python -m artiq.compiler.testbench.jit %s
#
# Check various sret-ized return types integrate properly with try/finally, which lowers
# to `invoke` on the LLVM level (code adapted from GitHub #1506).
#
LIST = [1, 2]
def get_tuple():
return (1, 2)
def get_list():
return LIST
def get_range():
return range(10)
def main():
try:
a, b = get_tuple()
assert a == 1
assert b == 2
finally:
pass
try:
for _ in get_list():
pass
finally:
pass
try:
for _ in get_range():
pass
finally:
pass
main()

View File

@ -0,0 +1,13 @@
# RUN: %python -m artiq.compiler.testbench.embedding %s
from artiq.language.core import *
from artiq.language.types import *
import numpy as np
n = 2
data = np.zeros((n, n))
@kernel
def entrypoint():
print(data[:n])

View File

@ -0,0 +1,19 @@
# RUN: %python -m artiq.compiler.testbench.embedding %s
from artiq.language.core import *
from artiq.language.types import *
import numpy as np
class A:
def __init__(self):
self.n = 2
@kernel
def run(self):
print([1, 2, 3][:self.n])
a = A()
@kernel
def entrypoint():
a.run()

View File

@ -45,6 +45,8 @@ The Python types correspond to ARTIQ type annotations as follows:
+---------------+-------------------------+ +---------------+-------------------------+
| list of T | TList(T) | | list of T | TList(T) |
+---------------+-------------------------+ +---------------+-------------------------+
| NumPy array | TArray(T, num_dims) |
+---------------+-------------------------+
| range | TRange32, TRange64 | | range | TRange32, TRange64 |
+---------------+-------------------------+ +---------------+-------------------------+
| numpy.int32 | TInt32 | | numpy.int32 | TInt32 |

View File

@ -20,11 +20,10 @@ from unittest.mock import Mock
import sphinx_rtd_theme import sphinx_rtd_theme
# Hack-patch Sphinx so that ARTIQ-Python types are correctly printed # Ensure that ARTIQ-Python types are correctly printed
# See: https://github.com/m-labs/artiq/issues/741 # See: https://github.com/m-labs/artiq/issues/741
from sphinx.ext import autodoc import builtins
from sphinx.util import inspect builtins.__in_sphinx__ = True
autodoc.repr = inspect.repr = str
# we cannot use autodoc_mock_imports (does not help with argparse) # we cannot use autodoc_mock_imports (does not help with argparse)

View File

@ -19,5 +19,10 @@ ARTIQ itself does not depend on Nix, and it is also possible to compile everythi
* Check that the board boots and examine the UART messages by running a serial terminal program, e.g. ``$ flterm /dev/ttyUSB1`` (``flterm`` is part of MiSoC and installed by ``shell-dev.nix``). Leave the terminal running while you are flashing the board, so that you see the startup messages when the board boots immediately after flashing. You can also restart the board (without reflashing it) with ``$ artiq_flash start``. * Check that the board boots and examine the UART messages by running a serial terminal program, e.g. ``$ flterm /dev/ttyUSB1`` (``flterm`` is part of MiSoC and installed by ``shell-dev.nix``). Leave the terminal running while you are flashing the board, so that you see the startup messages when the board boots immediately after flashing. You can also restart the board (without reflashing it) with ``$ artiq_flash start``.
* The communication parameters are 115200 8-N-1. Ensure that your user has access to the serial device (e.g. by adding the user account to the ``dialout`` group). * The communication parameters are 115200 8-N-1. Ensure that your user has access to the serial device (e.g. by adding the user account to the ``dialout`` group).
.. note::
If you do not plan to modify ``nix-scripts``, with the ARTIQ channel configured you can simply enter the development Nix shell with ``nix-shell "<artiq-full/fast/shell-dev.nix>"``. No repositories need to be cloned. This is especially useful if you simply want to build firmware using an unmodified version of ARTIQ.
.. warning:: .. warning::
Nix will make a read-only copy of the ARTIQ source to use in the shell environment. Therefore, any modifications that you make to the source after the shell is started will not be taken into account. A solution applicable to ARTIQ (and several other Python packages such as Migen and MiSoC) is to prepend the ARTIQ source directory to the ``PYTHONPATH`` environment variable after entering the shell. If you want this to be done by default, edit ``profile`` in ``shell-dev.nix``. Nix will make a read-only copy of the ARTIQ source to use in the shell environment. Therefore, any modifications that you make to the source after the shell is started will not be taken into account. A solution applicable to ARTIQ (and several other Python packages such as Migen and MiSoC) is to prepend the ARTIQ source directory to the ``PYTHONPATH`` environment variable after entering the shell. If you want this to be done by default, edit ``profile`` in ``shell-dev.nix``.

View File

@ -132,7 +132,7 @@ We suggest that you define a function ``get_argparser`` that returns the argumen
Logging Logging
------- -------
For the debug, information and warning messages, use the ``logging`` Python module and print the log on the standard error output (the default setting). The logging level is by default "WARNING", meaning that only warning messages and more critical messages will get printed (and no debug nor information messages). By calling ``sipyco.common_args.verbosity_args`` with the parser as argument, you add support for the ``--verbose`` (``-v``) and ``--quiet`` (``-q``) arguments in the parser. Each occurence of ``-v`` (resp. ``-q``) in the arguments will increase (resp. decrease) the log level of the logging module. For instance, if only one ``-v`` is present in the arguments, then more messages (info, warning and above) will get printed. If only one ``-q`` is present in the arguments, then only errors and critical messages will get printed. If ``-qq`` is present in the arguments, then only critical messages will get printed, but no debug/info/warning/error. For the debug, information and warning messages, use the ``logging`` Python module and print the log on the standard error output (the default setting). The logging level is by default "WARNING", meaning that only warning messages and more critical messages will get printed (and no debug nor information messages). By calling ``sipyco.common_args.verbosity_args`` with the parser as argument, you add support for the ``--verbose`` (``-v``) and ``--quiet`` (``-q``) arguments in the parser. Each occurrence of ``-v`` (resp. ``-q``) in the arguments will increase (resp. decrease) the log level of the logging module. For instance, if only one ``-v`` is present in the arguments, then more messages (info, warning and above) will get printed. If only one ``-q`` is present in the arguments, then only errors and critical messages will get printed. If ``-qq`` is present in the arguments, then only critical messages will get printed, but no debug/info/warning/error.
The program below exemplifies how to use logging: :: The program below exemplifies how to use logging: ::

View File

@ -21,12 +21,12 @@ First, install the Nix package manager. Some distributions provide a package for
Once Nix is installed, add the M-Labs package channel with: :: Once Nix is installed, add the M-Labs package channel with: ::
$ nix-channel --add https://nixbld.m-labs.hk/channel/custom/artiq/full-beta/artiq-full $ nix-channel --add https://nixbld.m-labs.hk/channel/custom/artiq/full-legacy/artiq-full
Those channels track `nixpkgs 20.09 <https://github.com/NixOS/nixpkgs/tree/release-20.09>`_. You can check the latest status through the `Hydra interface <https://nixbld.m-labs.hk>`_. As the Nix package manager default installation uses the development version of nixpkgs, we need to tell it to switch to the release: :: Those channels track `nixpkgs 21.05 <https://github.com/NixOS/nixpkgs/tree/release-21.05>`_. You can check the latest status through the `Hydra interface <https://nixbld.m-labs.hk>`_. As the Nix package manager default installation uses the development version of nixpkgs, we need to tell it to switch to the release: ::
$ nix-channel --remove nixpkgs $ nix-channel --remove nixpkgs
$ nix-channel --add https://nixos.org/channels/nixos-20.09 nixpkgs $ nix-channel --add https://nixos.org/channels/nixos-21.05 nixpkgs
Finally, make all the channel changes effective: :: Finally, make all the channel changes effective: ::
@ -118,13 +118,19 @@ Controllers for third-party devices (e.g. Thorlabs TCube, Lab Brick Digital Atte
Set up the Conda channel and install ARTIQ into a new Conda environment: :: Set up the Conda channel and install ARTIQ into a new Conda environment: ::
$ conda config --prepend channels https://conda.m-labs.hk/artiq-beta $ conda config --prepend channels https://conda.m-labs.hk/artiq-legacy
$ conda config --append channels conda-forge $ conda config --append channels conda-forge
$ conda create -n artiq artiq $ conda create -n artiq artiq
.. note:: .. note::
If you do not need to flash boards, the ``artiq`` package is sufficient. The packages named ``artiq-board-*`` contain only firmware for the FPGA board, and you should not install them unless you are reflashing an FPGA board. Controllers for third-party devices (e.g. Thorlabs TCube, Lab Brick Digital Attenuator, etc.) that are not shipped with ARTIQ can also be installed with Conda. Browse `Hydra <https://nixbld.m-labs.hk/project/artiq>`_ or see the list of NDSPs in this manual to find the names of the corresponding packages. If you do not need to flash boards, the ``artiq`` package is sufficient. The packages named ``artiq-board-*`` contain only firmware for the FPGA board, and you should not install them unless you are reflashing an FPGA board. Controllers for third-party devices (e.g. Thorlabs TCube, Lab Brick Digital Attenuator, etc.) that are not shipped with ARTIQ can also be installed with Conda. Browse `Hydra <https://nixbld.m-labs.hk/project/artiq>`_ or see the list of NDSPs in this manual to find the names of the corresponding packages.
.. note::
On Windows, if the last command that creates and installs the ARTIQ environment fails with an error similar to "seeking backwards is not allowed", try to re-run the command with admin rights.
.. note::
For commercial use you might need a license for Anaconda/Miniconda or for using the Anaconda package channel. `Miniforge <https://github.com/conda-forge/miniforge>`_ might be an alternative in a commercial environment as it does not include the Anaconda package channel by default. If you want to use Anaconda/Miniconda/Miniforge in a commercial environment, please check the license and the latest terms of service.
After the installation, activate the newly created environment by name. :: After the installation, activate the newly created environment by name. ::
$ conda activate artiq $ conda activate artiq
@ -180,9 +186,9 @@ OpenOCD can be used to write the binary images into the core device FPGA board's
With Nix, add ``artiq-full.openocd`` to the shell packages. Be careful not to add ``pkgs.openocd`` instead - this would install OpenOCD from the NixOS package collection, which does not support ARTIQ boards. With Nix, add ``artiq-full.openocd`` to the shell packages. Be careful not to add ``pkgs.openocd`` instead - this would install OpenOCD from the NixOS package collection, which does not support ARTIQ boards.
With Conda, the ``artiq`` package installs ``openocd`` automatically but it can also be installed explicitly on both Linux and Windows:: With Conda, install ``openocd`` as follows::
$ conda install openocd $ conda install -c m-labs openocd
.. _configuring-openocd: .. _configuring-openocd:

View File

@ -81,11 +81,11 @@ You can write several records at once::
To remove the previously written key ``my_key``:: To remove the previously written key ``my_key``::
$ artiq_coremgmt config delete my_key $ artiq_coremgmt config remove my_key
You can remove several keys at once:: You can remove several keys at once::
$ artiq_coremgmt config delete key1 key2 $ artiq_coremgmt config remove key1 key2
To erase the entire flash storage area:: To erase the entire flash storage area::

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

View File

@ -64,10 +64,6 @@ Topic :: System :: Hardware
""".splitlines(), """.splitlines(),
install_requires=requirements, install_requires=requirements,
extras_require={}, extras_require={},
dependency_links=[
"git+https://github.com/m-labs/pyqtgraph.git@develop#egg=pyqtgraph",
"git+https://github.com/m-labs/llvmlite.git@artiq#egg=llvmlite_artiq"
],
packages=find_packages(), packages=find_packages(),
namespace_packages=[], namespace_packages=[],
include_package_data=True, include_package_data=True,