forked from M-Labs/artiq
Compare commits
106 Commits
Author | SHA1 | Date |
---|---|---|
mwojcik | 9a3f6f85a4 | |
David Nadlinger | 81d2b37b57 | |
Jonathan Coates | f416b637c6 | |
Sebastien Bourdeauducq | 76ee719a0d | |
sven-oxionics | 2d017a05a1 | |
Florian Agbuya | c73601b4b3 | |
mwojcik | 9e6ffcfa30 | |
Sebastien Bourdeauducq | 837dffb56c | |
Charles Baynham | 76e93dc591 | |
Jonathan Coates | cfc396cbc8 | |
Hartmann Michael (IFAG PSS SIS SCE QSE) | 9c12d8b1de | |
Ikko Eltociear Ashimine | 8bd1f5c8b9 | |
Ikko Eltociear Ashimine | 671ed8ac77 | |
火焚 富良 | b50d30ba5b | |
cc78078 | 1b3fa3429d | |
cc78078 | 004e7e7461 | |
Sebastien Bourdeauducq | ad37be40c7 | |
Sebastien Bourdeauducq | a3ca686305 | |
Deepskyhunter | 20dc923c9e | |
Sebastien Bourdeauducq | 83af3b756d | |
Sebastien Bourdeauducq | 3bc5a0dfb7 | |
David Nadlinger | 73371931fc | |
David Nadlinger | 8786b137a3 | |
Leon Riesebos | ca67ae8365 | |
Leon Riesebos | 0226259e2b | |
pca006132 | c6a7b8a8dd | |
pca006132 | 66b6555c72 | |
Sebastien Bourdeauducq | 58f69cc96e | |
mwojcik | 2e65574c5f | |
ciciwu | 66ea41a81c | |
Sebastien Bourdeauducq | 56b8c3c096 | |
Steve Fan | f941e17107 | |
Steve Fan | d7838e16dd | |
Steve Fan | 57e2ec629b | |
Peter Drmota | 4f87531565 | |
Steve Fan | 64347290fb | |
Harry Ho | f49f1fcbfc | |
Harry Ho | 86fcd97416 | |
Steve Fan | 759f00416b | |
Harry Ho | 5818bc0878 | |
Harry Ho | 44171258f5 | |
Harry Ho | bc249c32df | |
Harry Ho | 949917cc9c | |
Harry Ho | dc411d55be | |
Harry Ho | 40e7b6058e | |
Harry Ho | 105af644bd | |
Harry Ho | 04ee775a9f | |
Sébastien Bourdeauducq | 9547a15162 | |
Harry Ho | d3869c966e | |
Harry Ho | 77c4d2f013 | |
Harry Ho | 23f5796d67 | |
Harry Ho | 34e89a3777 | |
Harry Ho | a14666bc15 | |
Harry Ho | 2f49a1a412 | |
Harry Ho | 412936f8db | |
Harry Ho | 51e28de2f6 | |
Harry Ho | f5b9eab84b | |
Harry Ho | 9dfb0bfe1b | |
Sebastien Bourdeauducq | 946254d22e | |
Sebastien Bourdeauducq | d9b01ed81a | |
Sebastien Bourdeauducq | 9801aeb6a5 | |
Sebastien Bourdeauducq | 08b09f6dc3 | |
Sebastien Bourdeauducq | dda4121c1d | |
Sebastien Bourdeauducq | 19daf91280 | |
Sebastien Bourdeauducq | 281b2182da | |
Star Chen | 414080554c | |
StarChen | 7b523084b7 | |
Sebastien Bourdeauducq | c4902be6f8 | |
occheung | c6cd9ac2ea | |
Sebastien Bourdeauducq | 9741e4aa43 | |
Leon Riesebos | ae137d1c9e | |
Star Chen | 2f808880d5 | |
pca006132 | 9033c59b75 | |
Sebastien Bourdeauducq | a80c35a606 | |
pca006132 | 93e1bd9ba0 | |
David Nadlinger | 65f0951f1a | |
Sebastien Bourdeauducq | 040aa6fd9d | |
Sebastien Bourdeauducq | a16c81a069 | |
Peter Drmota | ec4270fb4b | |
Leon Riesebos | 2d4fefe42e | |
Leon Riesebos | 1619a32a1e | |
Leon Riesebos | d000e06fbc | |
Marius Weber | 95e292c8a2 | |
Harry Ho | 0e3f23a86a | |
Harry Ho | dbeea7605b | |
Harry Ho | c6bfcdbf10 | |
Harry Ho | cd2e471b07 | |
Sebastien Bourdeauducq | a23645291c | |
David Nadlinger | 3f6840b736 | |
David Nadlinger | 9a9290a72d | |
David Nadlinger | c1733eef49 | |
David Nadlinger | 9e3b6faceb | |
David Nadlinger | 01352236ee | |
David Nadlinger | ca6db87895 | |
David Nadlinger | c1413a9945 | |
David Nadlinger | 925014689e | |
David Nadlinger | 8a892af244 | |
David Nadlinger | 5dcd73107c | |
Sebastien Bourdeauducq | c22482787e | |
Sebastien Bourdeauducq | cdd27249a2 | |
Sebastien Bourdeauducq | 6861d3ab33 | |
Sebastien Bourdeauducq | d180a1b3af | |
Harry Ho | d74cd24d89 | |
Harry Ho | 400af2c582 | |
Harry Ho | b5405dfad6 | |
Sebastien Bourdeauducq | 433c3bb8f9 |
|
@ -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 isinstance(v, h5py.Dataset):
|
||||||
|
rd[k] = (True, v[()])
|
||||||
|
|
||||||
|
f["archive"].visititems(visitor)
|
||||||
|
|
||||||
if "datasets" in f:
|
if "datasets" in f:
|
||||||
for k, v in f["datasets"].items():
|
def visitor(k, v):
|
||||||
if k in rd:
|
if isinstance(v, h5py.Dataset):
|
||||||
logger.warning("dataset '%s' is both in archive and "
|
if k in rd:
|
||||||
"outputs", k)
|
logger.warning("dataset '%s' is both in archive "
|
||||||
rd[k] = (True, v[()])
|
"and outputs", k)
|
||||||
if rd:
|
rd[k] = (True, v[()])
|
||||||
self.datasets.init(rd)
|
|
||||||
|
f["datasets"].visititems(visitor)
|
||||||
|
|
||||||
|
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):
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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,91 +1145,127 @@ 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 node.slice.step is not None:
|
if builtins.is_array(node.type):
|
||||||
try:
|
# To implement strided slicing with the proper NumPy reference
|
||||||
old_assign, self.current_assign = self.current_assign, None
|
# semantics, the pointer/length array representation will need to be
|
||||||
step = self.visit(node.slice.step)
|
# extended by another field to hold a variable stride.
|
||||||
finally:
|
assert node.slice.step is None, (
|
||||||
self.current_assign = old_assign
|
"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:
|
||||||
|
try:
|
||||||
|
old_assign, self.current_assign = self.current_assign, None
|
||||||
|
step = self.visit(node.slice.step)
|
||||||
|
finally:
|
||||||
|
self.current_assign = old_assign
|
||||||
|
|
||||||
|
self._make_check(
|
||||||
|
self.append(ir.Compare(ast.NotEq(loc=None), step, ir.Constant(0, step.type))),
|
||||||
|
lambda: self.alloc_exn(builtins.TException("ValueError"),
|
||||||
|
ir.Constant("step cannot be zero", builtins.TStr())),
|
||||||
|
loc=node.slice.step.loc)
|
||||||
|
else:
|
||||||
|
step = ir.Constant(1, node.slice.type)
|
||||||
|
counting_up = self.append(ir.Compare(ast.Gt(loc=None), step,
|
||||||
|
ir.Constant(0, step.type)))
|
||||||
|
|
||||||
|
unstepped_size = self.append(ir.Arith(ast.Sub(loc=None),
|
||||||
|
mapped_stop_index, mapped_start_index))
|
||||||
|
slice_size_a = self.append(ir.Arith(ast.FloorDiv(loc=None), unstepped_size, step))
|
||||||
|
slice_size_b = self.append(ir.Arith(ast.Mod(loc=None), unstepped_size, step))
|
||||||
|
rem_not_empty = self.append(ir.Compare(ast.NotEq(loc=None), slice_size_b,
|
||||||
|
ir.Constant(0, slice_size_b.type)))
|
||||||
|
slice_size_c = self.append(ir.Arith(ast.Add(loc=None), slice_size_a,
|
||||||
|
ir.Constant(1, slice_size_a.type)))
|
||||||
|
slice_size = self.append(ir.Select(rem_not_empty,
|
||||||
|
slice_size_c, slice_size_a,
|
||||||
|
name="slice.size"))
|
||||||
self._make_check(
|
self._make_check(
|
||||||
self.append(ir.Compare(ast.NotEq(loc=None), step, ir.Constant(0, step.type))),
|
self.append(ir.Compare(ast.LtE(loc=None), slice_size, length)),
|
||||||
lambda: self.alloc_exn(builtins.TException("ValueError"),
|
lambda slice_size, length: self.alloc_exn(builtins.TException("ValueError"),
|
||||||
ir.Constant("step cannot be zero", builtins.TStr())),
|
ir.Constant("slice size {0} is larger than iterable length {1}",
|
||||||
loc=node.slice.step.loc)
|
builtins.TStr()),
|
||||||
else:
|
slice_size, length),
|
||||||
step = ir.Constant(1, node.slice.type)
|
params=[slice_size, length],
|
||||||
counting_up = self.append(ir.Compare(ast.Gt(loc=None), step,
|
loc=node.slice.loc)
|
||||||
ir.Constant(0, step.type)))
|
|
||||||
|
|
||||||
unstepped_size = self.append(ir.Arith(ast.Sub(loc=None),
|
if self.current_assign is None:
|
||||||
mapped_stop_index, mapped_start_index))
|
is_neg_size = self.append(ir.Compare(ast.Lt(loc=None),
|
||||||
slice_size_a = self.append(ir.Arith(ast.FloorDiv(loc=None), unstepped_size, step))
|
slice_size, ir.Constant(0, slice_size.type)))
|
||||||
slice_size_b = self.append(ir.Arith(ast.Mod(loc=None), unstepped_size, step))
|
abs_slice_size = self.append(ir.Select(is_neg_size,
|
||||||
rem_not_empty = self.append(ir.Compare(ast.NotEq(loc=None), slice_size_b,
|
ir.Constant(0, slice_size.type), slice_size))
|
||||||
ir.Constant(0, slice_size_b.type)))
|
other_value = self.append(ir.Alloc([abs_slice_size], value.type,
|
||||||
slice_size_c = self.append(ir.Arith(ast.Add(loc=None), slice_size_a,
|
name="slice.result"))
|
||||||
ir.Constant(1, slice_size_a.type)))
|
else:
|
||||||
slice_size = self.append(ir.Select(rem_not_empty,
|
other_value = self.current_assign
|
||||||
slice_size_c, slice_size_a,
|
|
||||||
name="slice.size"))
|
|
||||||
self._make_check(
|
|
||||||
self.append(ir.Compare(ast.LtE(loc=None), slice_size, length)),
|
|
||||||
lambda slice_size, length: self.alloc_exn(builtins.TException("ValueError"),
|
|
||||||
ir.Constant("slice size {0} is larger than iterable length {1}",
|
|
||||||
builtins.TStr()),
|
|
||||||
slice_size, length),
|
|
||||||
params=[slice_size, length],
|
|
||||||
loc=node.slice.loc)
|
|
||||||
|
|
||||||
if self.current_assign is None:
|
prehead = self.current_block
|
||||||
is_neg_size = self.append(ir.Compare(ast.Lt(loc=None),
|
|
||||||
slice_size, ir.Constant(0, slice_size.type)))
|
|
||||||
abs_slice_size = self.append(ir.Select(is_neg_size,
|
|
||||||
ir.Constant(0, slice_size.type), slice_size))
|
|
||||||
other_value = self.append(ir.Alloc([abs_slice_size], value.type,
|
|
||||||
name="slice.result"))
|
|
||||||
else:
|
|
||||||
other_value = self.current_assign
|
|
||||||
|
|
||||||
prehead = self.current_block
|
head = self.current_block = self.add_block("slice.head")
|
||||||
|
prehead.append(ir.Branch(head))
|
||||||
|
|
||||||
head = self.current_block = self.add_block("slice.head")
|
index = self.append(ir.Phi(node.slice.type,
|
||||||
prehead.append(ir.Branch(head))
|
name="slice.index"))
|
||||||
|
index.add_incoming(mapped_start_index, prehead)
|
||||||
|
other_index = self.append(ir.Phi(node.slice.type,
|
||||||
|
name="slice.resindex"))
|
||||||
|
other_index.add_incoming(ir.Constant(0, node.slice.type), prehead)
|
||||||
|
|
||||||
index = self.append(ir.Phi(node.slice.type,
|
# Still within bounds?
|
||||||
name="slice.index"))
|
bounded_up = self.append(ir.Compare(ast.Lt(loc=None), index, mapped_stop_index))
|
||||||
index.add_incoming(mapped_start_index, prehead)
|
bounded_down = self.append(ir.Compare(ast.Gt(loc=None), index, mapped_stop_index))
|
||||||
other_index = self.append(ir.Phi(node.slice.type,
|
within_bounds = self.append(ir.Select(counting_up, bounded_up, bounded_down))
|
||||||
name="slice.resindex"))
|
|
||||||
other_index.add_incoming(ir.Constant(0, node.slice.type), prehead)
|
|
||||||
|
|
||||||
# Still within bounds?
|
body = self.current_block = self.add_block("slice.body")
|
||||||
bounded_up = self.append(ir.Compare(ast.Lt(loc=None), index, mapped_stop_index))
|
|
||||||
bounded_down = self.append(ir.Compare(ast.Gt(loc=None), index, mapped_stop_index))
|
|
||||||
within_bounds = self.append(ir.Select(counting_up, bounded_up, bounded_down))
|
|
||||||
|
|
||||||
body = self.current_block = self.add_block("slice.body")
|
if self.current_assign is None:
|
||||||
|
elem = self.iterable_get(value, index)
|
||||||
|
self.append(ir.SetElem(other_value, other_index, elem))
|
||||||
|
else:
|
||||||
|
elem = self.append(ir.GetElem(self.current_assign, other_index))
|
||||||
|
self.append(ir.SetElem(value, index, elem))
|
||||||
|
|
||||||
if self.current_assign is None:
|
next_index = self.append(ir.Arith(ast.Add(loc=None), index, step))
|
||||||
elem = self.iterable_get(value, index)
|
index.add_incoming(next_index, body)
|
||||||
self.append(ir.SetElem(other_value, other_index, elem))
|
next_other_index = self.append(ir.Arith(ast.Add(loc=None), other_index,
|
||||||
else:
|
ir.Constant(1, node.slice.type)))
|
||||||
elem = self.append(ir.GetElem(self.current_assign, other_index))
|
other_index.add_incoming(next_other_index, body)
|
||||||
self.append(ir.SetElem(value, index, elem))
|
self.append(ir.Branch(head))
|
||||||
|
|
||||||
next_index = self.append(ir.Arith(ast.Add(loc=None), index, step))
|
tail = self.current_block = self.add_block("slice.tail")
|
||||||
index.add_incoming(next_index, body)
|
head.append(ir.BranchIf(within_bounds, body, tail))
|
||||||
next_other_index = self.append(ir.Arith(ast.Add(loc=None), other_index,
|
|
||||||
ir.Constant(1, node.slice.type)))
|
|
||||||
other_index.add_incoming(next_other_index, body)
|
|
||||||
self.append(ir.Branch(head))
|
|
||||||
|
|
||||||
tail = self.current_block = self.add_block("slice.tail")
|
if self.current_assign is None:
|
||||||
head.append(ir.BranchIf(within_bounds, body, tail))
|
return other_value
|
||||||
|
|
||||||
if self.current_assign is None:
|
|
||||||
return other_value
|
|
||||||
|
|
||||||
def visit_TupleT(self, node):
|
def visit_TupleT(self, node):
|
||||||
if self.current_assign is None:
|
if self.current_assign is None:
|
||||||
|
|
|
@ -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,28 +914,45 @@ 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
|
||||||
|
|
||||||
# In the absence of any other information (there currently isn't a way
|
num_empty_dims = is_nested_empty_list(arg)
|
||||||
# to specify any), assume that all iterables are expandable into a
|
if num_empty_dims is not None:
|
||||||
# (runtime-checked) rectangular array of the innermost element type.
|
# As a special case, following the behaviour of numpy.array (and
|
||||||
elt = arg.type
|
# repr() on ndarrays), consider empty lists to be exactly of the
|
||||||
num_dims = 0
|
# number of dimensions given, instead of potentially containing an
|
||||||
result_dims = (node.type.find()["num_dims"].value
|
# unknown number of extra dimensions.
|
||||||
if builtins.is_array(node.type) else -1)
|
num_dims = num_empty_dims
|
||||||
while True:
|
|
||||||
if num_dims == result_dims:
|
# The ultimate element type will be TVar initially, but we might be
|
||||||
# If we already know the number of dimensions of the result,
|
# able to resolve it from context.
|
||||||
# stop so we can disambiguate the (innermost) element type of
|
elt = arg.type
|
||||||
# the argument if it is still unknown (e.g. empty array).
|
for _ in range(num_dims):
|
||||||
break
|
assert builtins.is_list(elt)
|
||||||
if types.is_var(elt):
|
elt = elt.find()["elt"]
|
||||||
return # undetermined yet
|
else:
|
||||||
if not builtins.is_iterable(elt) or builtins.is_str(elt):
|
# In the absence of any other information (there currently isn't a way
|
||||||
break
|
# to specify any), assume that all iterables are expandable into a
|
||||||
if builtins.is_array(elt):
|
# (runtime-checked) rectangular array of the innermost element type.
|
||||||
num_dims += elt.find()["num_dims"].value
|
elt = arg.type
|
||||||
else:
|
num_dims = 0
|
||||||
num_dims += 1
|
expected_dims = (node.type.find()["num_dims"].value
|
||||||
elt = builtins.get_iterable_elt(elt)
|
if builtins.is_array(node.type) else -1)
|
||||||
|
while True:
|
||||||
|
if num_dims == expected_dims:
|
||||||
|
# If we already know the number of dimensions of the result,
|
||||||
|
# stop so we can disambiguate the (innermost) element type of
|
||||||
|
# the argument if it is still unknown.
|
||||||
|
break
|
||||||
|
if types.is_var(elt):
|
||||||
|
# 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):
|
||||||
|
break
|
||||||
|
if builtins.is_array(elt):
|
||||||
|
num_dims += elt.find()["num_dims"].value
|
||||||
|
else:
|
||||||
|
num_dims += 1
|
||||||
|
elt = builtins.get_iterable_elt(elt)
|
||||||
|
|
||||||
if explicit_dtype is not None:
|
if explicit_dtype is not None:
|
||||||
# TODO: Factor out type detection; support quoted type constructors
|
# TODO: Factor out type detection; support quoted type constructors
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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).
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -175,24 +175,24 @@
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
"refclk": {
|
"refclk": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"minimum": 0
|
"minimum": 0
|
||||||
},
|
},
|
||||||
"clk_sel": {
|
"clk_sel": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 3
|
"maximum": 3
|
||||||
},
|
},
|
||||||
"clk_div": {
|
"clk_div": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 3
|
"maximum": 3
|
||||||
},
|
},
|
||||||
"pll_n": {
|
"pll_n": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"pll_vco": {
|
"pll_vco": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"dds": {
|
"dds": {
|
||||||
|
@ -282,20 +282,20 @@
|
||||||
"minItems": 2,
|
"minItems": 2,
|
||||||
"maxItems": 2
|
"maxItems": 2
|
||||||
},
|
},
|
||||||
"refclk": {
|
"refclk": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"minimum": 0
|
"minimum": 0
|
||||||
},
|
},
|
||||||
"clk_sel": {
|
"clk_sel": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 3
|
"maximum": 3
|
||||||
},
|
},
|
||||||
"pll_n": {
|
"pll_n": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"default": 32
|
"default": 32
|
||||||
},
|
},
|
||||||
"pll_vco": {
|
"pll_vco": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -364,17 +364,25 @@
|
||||||
"minItems": 1,
|
"minItems": 1,
|
||||||
"maxItems": 1
|
"maxItems": 1
|
||||||
},
|
},
|
||||||
"refclk": {
|
"refclk": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"exclusiveMinimum": 0,
|
"exclusiveMinimum": 0,
|
||||||
"default": 100e6
|
"default": 100e6
|
||||||
},
|
},
|
||||||
"clk_sel": {
|
"clk_sel": {
|
||||||
"type": "integer",
|
"oneOf": [
|
||||||
"minimum": 0,
|
{
|
||||||
"maximum": 3,
|
"type": "integer",
|
||||||
|
"minimum": 0,
|
||||||
|
"maximum": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["xo", "mmcx", "sma"]
|
||||||
|
}
|
||||||
|
],
|
||||||
"default": 0
|
"default": 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["ports"]
|
"required": ["ports"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,20 +149,20 @@ pub mod hmc7043 {
|
||||||
|
|
||||||
// enabled, divider, output config, is sysref
|
// enabled, divider, output config, is sysref
|
||||||
const OUTPUT_CONFIG: [(bool, u16, u8, bool); 14] = [
|
const OUTPUT_CONFIG: [(bool, u16, u8, bool); 14] = [
|
||||||
(true, DAC_CLK_DIV, 0x08, false), // 0: DAC1_CLK
|
(true, DAC_CLK_DIV, 0x08, false), // 0: DAC1_CLK
|
||||||
(true, SYSREF_DIV, 0x01, true), // 1: DAC1_SYSREF
|
(true, SYSREF_DIV, 0x01, true), // 1: DAC1_SYSREF
|
||||||
(true, DAC_CLK_DIV, 0x08, false), // 2: DAC0_CLK
|
(true, DAC_CLK_DIV, 0x08, false), // 2: DAC0_CLK
|
||||||
(true, SYSREF_DIV, 0x01, true), // 3: DAC0_SYSREF
|
(true, SYSREF_DIV, 0x01, true), // 3: DAC0_SYSREF
|
||||||
(true, SYSREF_DIV, 0x10, true), // 4: AMC_FPGA_SYSREF0
|
(true, SYSREF_DIV, 0x10, true), // 4: AMC_FPGA_SYSREF0
|
||||||
(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
|
||||||
(false, 0, 0x10, false), // 12: unused
|
(false, 0, 0x10, false), // 12: unused
|
||||||
(false, FPGA_CLK_DIV, 0x10, true), // 13: RTM_FPGA_SYSREF1
|
(false, FPGA_CLK_DIV, 0x10, true), // 13: RTM_FPGA_SYSREF1
|
||||||
];
|
];
|
||||||
|
|
||||||
fn spi_setup() {
|
fn spi_setup() {
|
||||||
|
@ -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()?;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
@ -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(()),
|
||||||
|
}
|
||||||
|
}
|
|
@ -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")]
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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;
|
||||||
*ptr = CMutSlice::new(alloc(length)? as *mut u8, length);
|
if length > 0 {
|
||||||
reader.read_exact((*ptr).as_mut())?;
|
*ptr = CMutSlice::new(alloc(length)? as *mut u8, length);
|
||||||
|
reader.read_exact((*ptr).as_mut())?;
|
||||||
|
} else {
|
||||||
|
*ptr = CMutSlice::new(core::ptr::NonNull::<u8>::dangling().as_ptr(), 0);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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> {
|
||||||
test_ddmtd_stability(true, 4)?;
|
for i in 0..3 { // Allow resetting DDMTD core 2 times at max
|
||||||
test_ddmtd_stability(false, 1)?;
|
let result = {
|
||||||
test_slip_ddmtd()?;
|
test_ddmtd_stability(true, 4)?;
|
||||||
|
test_ddmtd_stability(false, 1)?;
|
||||||
|
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()?;
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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():
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -101,8 +101,9 @@ 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)
|
||||||
del self.ttl_outs[io_update_device]
|
if io_update_device is not None:
|
||||||
|
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"]
|
||||||
del self.ttl_outs[cnv_device]
|
del self.ttl_outs[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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
from artiq.gateware.drtio.wrpll.core import WRPLL
|
|
||||||
from artiq.gateware.drtio.wrpll.ddmtd import DDMTDSamplerExtFF, DDMTDSamplerGTP
|
|
|
@ -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)
|
|
||||||
]
|
|
|
@ -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")
|
|
||||||
)
|
|
|
@ -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
|
|
|
@ -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()
|
|
|
@ -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)
|
|
|
@ -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),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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"))
|
self.specials += AsyncResetSynchronizer(self.cd_jesd, self.jreset.storage)
|
||||||
else:
|
|
||||||
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,
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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()
|
|
@ -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()
|
||||||
|
|
|
@ -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,35 +596,18 @@ 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.submodules.siphaser = SiPhaser7Series(
|
||||||
self.drtio_transceiver,
|
si5324_clkin=platform.request("cdr_clk") if platform.hw_rev == "v2.0"
|
||||||
platform.request("cdr_clk_clean_fabric"))
|
else platform.request("si5324_clkin"),
|
||||||
helper_clk_pads = platform.request("ddmtd_helper_clk")
|
rx_synchronizer=self.rx_synchronizer,
|
||||||
self.submodules.wrpll = WRPLL(
|
ref_clk=self.crg.clk125_div2, ref_div2=True,
|
||||||
helper_clk_pads=helper_clk_pads,
|
rtio_clk_freq=rtio_clk_freq)
|
||||||
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"),
|
platform.add_false_path_constraints(
|
||||||
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"),
|
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
|
||||||
ddmtd_inputs=self.wrpll_sampler)
|
self.csr_devices.append("siphaser")
|
||||||
self.csr_devices.append("wrpll")
|
self.config["HAS_SI5324"] = None
|
||||||
# note: do not use self.wrpll.cd_helper.clk; otherwise, vivado craps out with:
|
self.config["SI5324_SOFT_RESET"] = None
|
||||||
# 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(
|
|
||||||
si5324_clkin=platform.request("cdr_clk") if platform.hw_rev == "v2.0"
|
|
||||||
else platform.request("si5324_clkin"),
|
|
||||||
rx_synchronizer=self.rx_synchronizer,
|
|
||||||
ref_clk=self.crg.clk125_div2, ref_div2=True,
|
|
||||||
rtio_clk_freq=rtio_clk_freq)
|
|
||||||
platform.add_false_path_constraints(
|
|
||||||
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
|
|
||||||
self.csr_devices.append("siphaser")
|
|
||||||
self.config["HAS_SI5324"] = None
|
|
||||||
self.config["SI5324_SOFT_RESET"] = None
|
|
||||||
|
|
||||||
gtp = self.drtio_transceiver.gtps[0]
|
gtp = self.drtio_transceiver.gtps[0]
|
||||||
platform.add_period_constraint(gtp.txoutclk, rtio_clk_period)
|
platform.add_period_constraint(gtp.txoutclk, rtio_clk_period)
|
||||||
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,39 +128,22 @@ 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(1)
|
||||||
self.comb += [
|
self.submodules.siphaser = SiPhaser7Series(
|
||||||
platform.request("filtered_clk_sel").eq(0),
|
si5324_clkin=platform.request("si5324_clkin"),
|
||||||
platform.request("ddmtd_main_dcxo_oe").eq(1),
|
rx_synchronizer=self.rx_synchronizer,
|
||||||
platform.request("ddmtd_helper_dcxo_oe").eq(1)
|
ultrascale=True,
|
||||||
]
|
rtio_clk_freq=rtio_clk_freq)
|
||||||
self.submodules.wrpll_sampler = DDMTDSamplerExtFF(
|
platform.add_false_path_constraints(
|
||||||
platform.request("ddmtd_inputs"))
|
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
|
||||||
self.submodules.wrpll = WRPLL(
|
self.csr_devices.append("siphaser")
|
||||||
helper_clk_pads=platform.request("ddmtd_helper_clk"),
|
self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n)
|
||||||
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"),
|
self.csr_devices.append("si5324_rst_n")
|
||||||
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"),
|
i2c = self.platform.request("i2c")
|
||||||
ddmtd_inputs=self.wrpll_sampler)
|
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
||||||
self.csr_devices.append("wrpll")
|
self.csr_devices.append("i2c")
|
||||||
platform.add_period_constraint(self.wrpll.cd_helper.clk, rtio_clk_period*0.99)
|
self.config["I2C_BUS_COUNT"] = 1
|
||||||
platform.add_false_path_constraints(self.crg.cd_sys.clk, self.wrpll.cd_helper.clk)
|
self.config["HAS_SI5324"] = None
|
||||||
else:
|
|
||||||
self.comb += platform.request("filtered_clk_sel").eq(1)
|
|
||||||
self.submodules.siphaser = SiPhaser7Series(
|
|
||||||
si5324_clkin=platform.request("si5324_clkin"),
|
|
||||||
rx_synchronizer=self.rx_synchronizer,
|
|
||||||
ultrascale=True,
|
|
||||||
rtio_clk_freq=rtio_clk_freq)
|
|
||||||
platform.add_false_path_constraints(
|
|
||||||
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
|
|
||||||
self.csr_devices.append("siphaser")
|
|
||||||
self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n)
|
|
||||||
self.csr_devices.append("si5324_rst_n")
|
|
||||||
i2c = self.platform.request("i2c")
|
|
||||||
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
|
||||||
self.csr_devices.append("i2c")
|
|
||||||
self.config["I2C_BUS_COUNT"] = 1
|
|
||||||
self.config["HAS_SI5324"] = None
|
|
||||||
|
|
||||||
gth = self.drtio_transceiver.gths[0]
|
gth = self.drtio_transceiver.gths[0]
|
||||||
platform.add_period_constraint(gth.txoutclk, rtio_clk_period/2)
|
platform.add_period_constraint(gth.txoutclk, rtio_clk_period/2)
|
||||||
|
@ -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,
|
||||||
phy = ttl_serdes_ultrascale.InOut(4, mcx_io.level)
|
wave_freq=9e6)
|
||||||
self.comb += mcx_io.direction.eq(phy.oe)
|
self.comb += mcx_io.direction.eq(1)
|
||||||
self.submodules += phy
|
elif ttlout:
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
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)
|
||||||
|
self.comb += mcx_io.direction.eq(phy.oe)
|
||||||
|
self.submodules += 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:
|
||||||
|
|
|
@ -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,41 +138,23 @@ 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 += [
|
self.comb += platform.request("filtered_clk_sel").eq(1)
|
||||||
platform.request("filtered_clk_sel").eq(0),
|
self.submodules.siphaser = SiPhaser7Series(
|
||||||
platform.request("ddmtd_main_dcxo_oe").eq(1),
|
si5324_clkin=platform.request("si5324_clkin"),
|
||||||
platform.request("ddmtd_helper_dcxo_oe").eq(1)
|
rx_synchronizer=self.rx_synchronizer,
|
||||||
]
|
ref_clk=self.crg.cd_sys.clk, ref_div2=True,
|
||||||
self.submodules.wrpll_sampler = DDMTDSamplerGTP(
|
rtio_clk_freq=rtio_clk_freq)
|
||||||
self.drtio_transceiver,
|
platform.add_false_path_constraints(
|
||||||
platform.request("cdr_clk_clean_fabric"))
|
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
|
||||||
self.submodules.wrpll = WRPLL(
|
self.csr_devices.append("siphaser")
|
||||||
helper_clk_pads=platform.request("ddmtd_helper_clk"),
|
self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n)
|
||||||
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"),
|
self.csr_devices.append("si5324_rst_n")
|
||||||
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"),
|
i2c = self.platform.request("i2c")
|
||||||
ddmtd_inputs=self.wrpll_sampler)
|
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
||||||
self.csr_devices.append("wrpll")
|
self.csr_devices.append("i2c")
|
||||||
platform.add_period_constraint(self.wrpll.cd_helper.clk, rtio_clk_period*0.99)
|
self.config["I2C_BUS_COUNT"] = 1
|
||||||
platform.add_false_path_constraints(self.crg.cd_sys.clk, self.wrpll.cd_helper.clk)
|
self.config["HAS_SI5324"] = None
|
||||||
platform.add_false_path_constraints(self.wrpll.cd_helper.clk, gtp.rxoutclk)
|
|
||||||
else:
|
|
||||||
self.comb += platform.request("filtered_clk_sel").eq(1)
|
|
||||||
self.submodules.siphaser = SiPhaser7Series(
|
|
||||||
si5324_clkin=platform.request("si5324_clkin"),
|
|
||||||
rx_synchronizer=self.rx_synchronizer,
|
|
||||||
ref_clk=self.crg.cd_sys.clk, ref_div2=True,
|
|
||||||
rtio_clk_freq=rtio_clk_freq)
|
|
||||||
platform.add_false_path_constraints(
|
|
||||||
self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output)
|
|
||||||
self.csr_devices.append("siphaser")
|
|
||||||
self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n)
|
|
||||||
self.csr_devices.append("si5324_rst_n")
|
|
||||||
i2c = self.platform.request("i2c")
|
|
||||||
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
|
||||||
self.csr_devices.append("i2c")
|
|
||||||
self.config["I2C_BUS_COUNT"] = 1
|
|
||||||
self.config["HAS_SI5324"] = None
|
|
||||||
|
|
||||||
platform.add_period_constraint(gtp.txoutclk, rtio_clk_period)
|
platform.add_period_constraint(gtp.txoutclk, rtio_clk_period)
|
||||||
platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period)
|
platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period)
|
||||||
|
@ -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))
|
||||||
|
|
|
@ -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)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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()])
|
|
|
@ -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)
|
|
|
@ -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:
|
||||||
|
|
|
@ -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("_")
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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()
|
|
@ -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())
|
|
@ -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])
|
||||||
|
|
|
@ -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, )
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
|
@ -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()
|
|
@ -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])
|
|
@ -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()
|
|
@ -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 |
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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``.
|
||||||
|
|
|
@ -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: ::
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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 |
4
setup.py
4
setup.py
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue