forked from M-Labs/artiq
Compare commits
106 Commits
Author | SHA1 | Date | |
---|---|---|---|
9a3f6f85a4 | |||
|
81d2b37b57 | ||
f416b637c6 | |||
76ee719a0d | |||
2d017a05a1 | |||
c73601b4b3 | |||
9e6ffcfa30 | |||
837dffb56c | |||
|
76e93dc591 | ||
cfc396cbc8 | |||
|
9c12d8b1de | ||
|
8bd1f5c8b9 | ||
|
671ed8ac77 | ||
|
b50d30ba5b | ||
1b3fa3429d | |||
|
004e7e7461 | ||
ad37be40c7 | |||
a3ca686305 | |||
20dc923c9e | |||
83af3b756d | |||
3bc5a0dfb7 | |||
|
73371931fc | ||
|
8786b137a3 | ||
|
ca67ae8365 | ||
|
0226259e2b | ||
c6a7b8a8dd | |||
66b6555c72 | |||
58f69cc96e | |||
2e65574c5f | |||
|
66ea41a81c | ||
56b8c3c096 | |||
|
f941e17107 | ||
|
d7838e16dd | ||
|
57e2ec629b | ||
|
4f87531565 | ||
64347290fb | |||
f49f1fcbfc | |||
86fcd97416 | |||
759f00416b | |||
5818bc0878 | |||
44171258f5 | |||
bc249c32df | |||
949917cc9c | |||
dc411d55be | |||
40e7b6058e | |||
105af644bd | |||
04ee775a9f | |||
9547a15162 | |||
d3869c966e | |||
77c4d2f013 | |||
23f5796d67 | |||
34e89a3777 | |||
a14666bc15 | |||
2f49a1a412 | |||
412936f8db | |||
51e28de2f6 | |||
f5b9eab84b | |||
9dfb0bfe1b | |||
946254d22e | |||
d9b01ed81a | |||
9801aeb6a5 | |||
08b09f6dc3 | |||
dda4121c1d | |||
19daf91280 | |||
281b2182da | |||
414080554c | |||
7b523084b7 | |||
c4902be6f8 | |||
c6cd9ac2ea | |||
9741e4aa43 | |||
|
ae137d1c9e | ||
|
2f808880d5 | ||
9033c59b75 | |||
a80c35a606 | |||
93e1bd9ba0 | |||
|
65f0951f1a | ||
040aa6fd9d | |||
a16c81a069 | |||
|
ec4270fb4b | ||
|
2d4fefe42e | ||
|
1619a32a1e | ||
|
d000e06fbc | ||
95e292c8a2 | |||
0e3f23a86a | |||
dbeea7605b | |||
c6bfcdbf10 | |||
cd2e471b07 | |||
a23645291c | |||
|
3f6840b736 | ||
|
9a9290a72d | ||
|
c1733eef49 | ||
|
9e3b6faceb | ||
|
01352236ee | ||
|
ca6db87895 | ||
|
c1413a9945 | ||
|
925014689e | ||
|
8a892af244 | ||
|
5dcd73107c | ||
c22482787e | |||
cdd27249a2 | |||
6861d3ab33 | |||
d180a1b3af | |||
d74cd24d89 | |||
400af2c582 | |||
b5405dfad6 | |||
433c3bb8f9 |
@ -188,17 +188,27 @@ class FilesDock(QtWidgets.QDockWidget):
|
||||
except:
|
||||
logger.warning("unable to read metadata from %s",
|
||||
info.filePath(), exc_info=True)
|
||||
rd = dict()
|
||||
|
||||
rd = {}
|
||||
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:
|
||||
for k, v in f["datasets"].items():
|
||||
if k in rd:
|
||||
logger.warning("dataset '%s' is both in archive and "
|
||||
"outputs", k)
|
||||
rd[k] = (True, v[()])
|
||||
if rd:
|
||||
self.datasets.init(rd)
|
||||
def visitor(k, v):
|
||||
if isinstance(v, h5py.Dataset):
|
||||
if k in rd:
|
||||
logger.warning("dataset '%s' is both in archive "
|
||||
"and outputs", k)
|
||||
rd[k] = (True, v[()])
|
||||
|
||||
f["datasets"].visititems(visitor)
|
||||
|
||||
self.datasets.init(rd)
|
||||
|
||||
self.dataset_changed.emit(info.filePath())
|
||||
|
||||
def list_activated(self, idx):
|
||||
|
@ -315,6 +315,9 @@ def is_iterable(typ):
|
||||
return is_listish(typ) or is_range(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):
|
||||
return TInt(types.TValue(8))
|
||||
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.
|
||||
"""
|
||||
|
||||
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 pythonparser import ast, algorithm, source, diagnostic, parse_buffer
|
||||
@ -45,7 +45,14 @@ class EmbeddingMap:
|
||||
self.object_forward_map = {}
|
||||
self.object_reverse_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.used_instance_type_names = set()
|
||||
self.used_constructor_type_names = set()
|
||||
|
||||
self.function_map = {}
|
||||
|
||||
# Modules
|
||||
@ -60,16 +67,6 @@ class EmbeddingMap:
|
||||
|
||||
# Types
|
||||
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
|
||||
# defined inside functions) do not pose a problem to the compiler. The two places which
|
||||
# cannot handle this are:
|
||||
@ -78,12 +75,29 @@ class EmbeddingMap:
|
||||
# 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
|
||||
# functions). Also, easier to debug.
|
||||
n = 0
|
||||
for host_type in self.type_map:
|
||||
instance_type, constructor_type = self.type_map[host_type]
|
||||
if instance_type.name == new_instance_type.name:
|
||||
n += 1
|
||||
new_instance_type.name = "{}.{}".format(new_instance_type.name, n)
|
||||
suffix = 0
|
||||
new_instance_name = instance_type.name
|
||||
new_constructor_name = constructor_type.name
|
||||
while True:
|
||||
if (new_instance_name not in self.used_instance_type_names
|
||||
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):
|
||||
count = 0
|
||||
@ -162,14 +176,15 @@ class ASTSynthesizer:
|
||||
typ = builtins.TNone()
|
||||
return asttyped.NameConstantT(value=value, type=typ,
|
||||
loc=self._add(repr(value)))
|
||||
elif value is True or value is False:
|
||||
elif isinstance(value, (bool, numpy.bool_)):
|
||||
typ = builtins.TBool()
|
||||
return asttyped.NameConstantT(value=value, type=typ,
|
||||
loc=self._add(repr(value)))
|
||||
elif value is numpy.float:
|
||||
coerced = bool(value)
|
||||
return asttyped.NameConstantT(value=coerced, type=typ,
|
||||
loc=self._add(repr(coerced)))
|
||||
elif value is float:
|
||||
typ = builtins.fn_float()
|
||||
return asttyped.NameConstantT(value=None, type=typ,
|
||||
loc=self._add("numpy.float"))
|
||||
loc=self._add("float"))
|
||||
elif value is numpy.int32:
|
||||
typ = builtins.fn_int32()
|
||||
return asttyped.NameConstantT(value=None, type=typ,
|
||||
@ -446,7 +461,7 @@ class StitchingASTTypedRewriter(ASTTypedRewriter):
|
||||
node = asttyped.QuotedFunctionDefT(
|
||||
typing_env=extractor.typing_env, globals_in_scope=extractor.global_,
|
||||
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,
|
||||
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,
|
||||
@ -522,7 +537,7 @@ class StitchingInferencer(Inferencer):
|
||||
self.engine.process(diag)
|
||||
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
|
||||
# overhead (i.e. synthesizing a source buffer), but has the advantage
|
||||
# of having the host-to-ARTIQ mapping code in only one place and
|
||||
@ -902,13 +917,11 @@ class Stitcher:
|
||||
|
||||
# Parse.
|
||||
source_buffer = source.Buffer(source_code, filename, first_line)
|
||||
lexer = source_lexer.Lexer(source_buffer, version=sys.version_info[0:2],
|
||||
diagnostic_engine=self.engine)
|
||||
lexer = source_lexer.Lexer(source_buffer, version=(3, 6), diagnostic_engine=self.engine)
|
||||
lexer.indent = [(initial_indent,
|
||||
source.Range(source_buffer, 0, len(initial_whitespace)),
|
||||
initial_whitespace)]
|
||||
parser = source_parser.Parser(lexer, version=sys.version_info[0:2],
|
||||
diagnostic_engine=self.engine)
|
||||
parser = source_parser.Parser(lexer, version=(3, 6), diagnostic_engine=self.engine)
|
||||
function_node = parser.file_input().body[0]
|
||||
|
||||
# Mangle the name, since we put everything into a single module.
|
||||
@ -948,6 +961,31 @@ class Stitcher:
|
||||
if annot is None:
|
||||
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):
|
||||
diag = diagnostic.Diagnostic("error",
|
||||
"type annotation for {kind}, '{annot}', is not an ARTIQ type",
|
||||
|
@ -1116,7 +1116,11 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
_readable_name(index))))
|
||||
if self.current_assign is None:
|
||||
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)
|
||||
|
||||
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,
|
||||
loc=node.begin_loc)
|
||||
|
||||
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
|
||||
if builtins.is_array(node.type):
|
||||
# To implement strided slicing with the proper NumPy reference
|
||||
# semantics, the pointer/length array representation will need to be
|
||||
# extended by another field to hold a variable stride.
|
||||
assert node.slice.step is None, (
|
||||
"array slices with non-trivial step "
|
||||
"should have been disallowed during type inference")
|
||||
|
||||
# One-dimensionally slicing an array only affects the outermost
|
||||
# dimension.
|
||||
shape = self.append(ir.GetAttr(value, "shape"))
|
||||
lengths = [
|
||||
self.append(ir.GetAttr(shape, i))
|
||||
for i in range(len(shape.type.elts))
|
||||
]
|
||||
|
||||
# Compute outermost length – zero for "backwards" indices.
|
||||
raw_len = self.append(
|
||||
ir.Arith(ast.Sub(loc=None), mapped_stop_index, mapped_start_index))
|
||||
is_neg_len = self.append(
|
||||
ir.Compare(ast.Lt(loc=None), raw_len, ir.Constant(0, raw_len.type)))
|
||||
outer_len = self.append(
|
||||
ir.Select(is_neg_len, ir.Constant(0, raw_len.type), raw_len))
|
||||
new_shape = self._make_array_shape([outer_len] + lengths[1:])
|
||||
|
||||
# Offset buffer pointer by start index (times stride for inner dims).
|
||||
stride = reduce(
|
||||
lambda l, r: self.append(ir.Arith(ast.Mult(loc=None), l, r)),
|
||||
lengths[1:], ir.Constant(1, lengths[0].type))
|
||||
offset = self.append(
|
||||
ir.Arith(ast.Mult(loc=None), stride, mapped_start_index))
|
||||
buffer = self.append(ir.GetAttr(value, "buffer"))
|
||||
new_buffer = self.append(ir.Offset(buffer, offset))
|
||||
|
||||
return self.append(ir.Alloc([new_buffer, new_shape], node.type))
|
||||
else:
|
||||
if node.slice.step is not None:
|
||||
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.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)))
|
||||
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)
|
||||
|
||||
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.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:
|
||||
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
|
||||
|
||||
if self.current_assign is None:
|
||||
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
|
||||
|
||||
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")
|
||||
prehead.append(ir.Branch(head))
|
||||
index = self.append(ir.Phi(node.slice.type,
|
||||
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,
|
||||
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)
|
||||
# Still within bounds?
|
||||
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))
|
||||
|
||||
# Still within bounds?
|
||||
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")
|
||||
|
||||
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:
|
||||
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))
|
||||
next_index = self.append(ir.Arith(ast.Add(loc=None), index, step))
|
||||
index.add_incoming(next_index, body)
|
||||
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))
|
||||
|
||||
next_index = self.append(ir.Arith(ast.Add(loc=None), index, step))
|
||||
index.add_incoming(next_index, body)
|
||||
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")
|
||||
head.append(ir.BranchIf(within_bounds, body, tail))
|
||||
|
||||
tail = self.current_block = self.add_block("slice.tail")
|
||||
head.append(ir.BranchIf(within_bounds, body, tail))
|
||||
|
||||
if self.current_assign is None:
|
||||
return other_value
|
||||
if self.current_assign is None:
|
||||
return other_value
|
||||
|
||||
def visit_TupleT(self, node):
|
||||
if self.current_assign is None:
|
||||
|
@ -8,6 +8,28 @@ from .. import asttyped, types, builtins
|
||||
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` infers types by recursively applying the unification
|
||||
@ -216,6 +238,7 @@ class Inferencer(algorithm.Visitor):
|
||||
value.loc, None)
|
||||
|
||||
def visit_SliceT(self, node):
|
||||
self.generic_visit(node)
|
||||
if (node.lower, node.upper, node.step) == (None, None, None):
|
||||
self._unify(node.type, builtins.TInt32(),
|
||||
node.loc, None)
|
||||
@ -268,12 +291,21 @@ class Inferencer(algorithm.Visitor):
|
||||
else:
|
||||
self._unify_iterable(element=node, collection=node.value)
|
||||
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)
|
||||
else: # ExtSlice
|
||||
pass # error emitted above
|
||||
|
||||
def visit_IfExpT(self, node):
|
||||
self.generic_visit(node)
|
||||
self._unify(node.test.type, builtins.TBool(), node.test.loc, None)
|
||||
self._unify(node.body.type, node.orelse.type,
|
||||
node.body.loc, node.orelse.loc)
|
||||
self._unify(node.type, node.body.type,
|
||||
@ -882,28 +914,45 @@ class Inferencer(algorithm.Visitor):
|
||||
if len(node.args) == 1 and keywords_acceptable:
|
||||
arg, = node.args
|
||||
|
||||
# In the absence of any other information (there currently isn't a way
|
||||
# to specify any), assume that all iterables are expandable into a
|
||||
# (runtime-checked) rectangular array of the innermost element type.
|
||||
elt = arg.type
|
||||
num_dims = 0
|
||||
result_dims = (node.type.find()["num_dims"].value
|
||||
if builtins.is_array(node.type) else -1)
|
||||
while True:
|
||||
if num_dims == result_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 (e.g. empty array).
|
||||
break
|
||||
if types.is_var(elt):
|
||||
return # undetermined yet
|
||||
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)
|
||||
num_empty_dims = is_nested_empty_list(arg)
|
||||
if num_empty_dims is not None:
|
||||
# As a special case, following the behaviour of numpy.array (and
|
||||
# repr() on ndarrays), consider empty lists to be exactly of the
|
||||
# number of dimensions given, instead of potentially containing an
|
||||
# unknown number of extra dimensions.
|
||||
num_dims = num_empty_dims
|
||||
|
||||
# The ultimate element type will be TVar initially, but we might be
|
||||
# able to resolve it from context.
|
||||
elt = arg.type
|
||||
for _ in range(num_dims):
|
||||
assert builtins.is_list(elt)
|
||||
elt = elt.find()["elt"]
|
||||
else:
|
||||
# In the absence of any other information (there currently isn't a way
|
||||
# to specify any), assume that all iterables are expandable into a
|
||||
# (runtime-checked) rectangular array of the innermost element type.
|
||||
elt = arg.type
|
||||
num_dims = 0
|
||||
expected_dims = (node.type.find()["num_dims"].value
|
||||
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:
|
||||
# TODO: Factor out type detection; support quoted type constructors
|
||||
|
@ -331,8 +331,8 @@ class LLVMIRGenerator:
|
||||
else:
|
||||
value = const.value
|
||||
|
||||
llptr = self.llstr_of_str(const.value, linkage="private", unnamed_addr=True)
|
||||
lllen = ll.Constant(lli32, len(const.value))
|
||||
llptr = self.llstr_of_str(value, linkage="private", unnamed_addr=True)
|
||||
lllen = ll.Constant(lli32, len(value))
|
||||
return ll.Constant(llty, (llptr, lllen))
|
||||
else:
|
||||
assert False
|
||||
@ -1328,13 +1328,13 @@ class LLVMIRGenerator:
|
||||
self.engine.process(diag)
|
||||
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()))
|
||||
lltagptr = self.llbuilder.alloca(lltag.type)
|
||||
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)),
|
||||
name="rpc.args")
|
||||
for index, arg in enumerate(args):
|
||||
@ -1474,19 +1474,22 @@ class LLVMIRGenerator:
|
||||
llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [])
|
||||
|
||||
llresultslot = self.llbuilder.alloca(llfun.type.pointee.args[0].pointee)
|
||||
llcall = self.llbuilder.invoke(llfun, llargs, llnormalblock, llunwindblock,
|
||||
name=insn.name)
|
||||
llcall = self.llbuilder.invoke(llfun, [llresultslot] + llargs,
|
||||
llnormalblock, llunwindblock, name=insn.name)
|
||||
|
||||
self.llbuilder.position_at_start(llnormalblock)
|
||||
llresult = self.llbuilder.load(llresultslot)
|
||||
|
||||
self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr])
|
||||
else:
|
||||
llcall = self.llbuilder.invoke(llfun, llargs, llnormalblock, llunwindblock,
|
||||
name=insn.name)
|
||||
llresult = llcall
|
||||
|
||||
# The !tbaa metadata is not legal to use with the invoke instruction,
|
||||
# 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):
|
||||
llelts = [self._quote(value[i], elt_type, lambda: path() + [str(i)])
|
||||
@ -1558,7 +1561,8 @@ class LLVMIRGenerator:
|
||||
return ll.Constant.literal_struct([])
|
||||
elif builtins.is_bool(typ):
|
||||
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):
|
||||
assert isinstance(value, (int, numpy.int32, numpy.int64)), fail_msg
|
||||
return ll.Constant(llty, int(value))
|
||||
|
@ -3,6 +3,7 @@ The :mod:`types` module contains the classes describing the types
|
||||
in :mod:`asttyped`.
|
||||
"""
|
||||
|
||||
import builtins
|
||||
import string
|
||||
from collections import OrderedDict
|
||||
from . import iodelay
|
||||
@ -97,6 +98,8 @@ class TVar(Type):
|
||||
return self.find().fold(accum, fn)
|
||||
|
||||
def __repr__(self):
|
||||
if getattr(builtins, "__in_sphinx__", False):
|
||||
return str(self)
|
||||
if self.parent is self:
|
||||
return "<artiq.compiler.types.TVar %d>" % id(self)
|
||||
else:
|
||||
@ -143,6 +146,8 @@ class TMono(Type):
|
||||
return fn(accum, 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))
|
||||
|
||||
def __getitem__(self, param):
|
||||
@ -191,6 +196,8 @@ class TTuple(Type):
|
||||
return fn(accum, self)
|
||||
|
||||
def __repr__(self):
|
||||
if getattr(builtins, "__in_sphinx__", False):
|
||||
return str(self)
|
||||
return "artiq.compiler.types.TTuple(%s)" % repr(self.elts)
|
||||
|
||||
def __eq__(self, other):
|
||||
@ -269,6 +276,8 @@ class TFunction(Type):
|
||||
return fn(accum, self)
|
||||
|
||||
def __repr__(self):
|
||||
if getattr(builtins, "__in_sphinx__", False):
|
||||
return str(self)
|
||||
return "artiq.compiler.types.TFunction({}, {}, {})".format(
|
||||
repr(self.args), repr(self.optargs), repr(self.ret))
|
||||
|
||||
@ -362,6 +371,8 @@ class TRPC(Type):
|
||||
return fn(accum, self)
|
||||
|
||||
def __repr__(self):
|
||||
if getattr(builtins, "__in_sphinx__", False):
|
||||
return str(self)
|
||||
return "artiq.compiler.types.TRPC({})".format(repr(self.ret))
|
||||
|
||||
def __eq__(self, other):
|
||||
@ -399,6 +410,8 @@ class TBuiltin(Type):
|
||||
return fn(accum, self)
|
||||
|
||||
def __repr__(self):
|
||||
if getattr(builtins, "__in_sphinx__", False):
|
||||
return str(self)
|
||||
return "artiq.compiler.types.{}({})".format(type(self).__name__, repr(self.name))
|
||||
|
||||
def __eq__(self, other):
|
||||
@ -459,6 +472,8 @@ class TInstance(TMono):
|
||||
self.constant_attributes = set()
|
||||
|
||||
def __repr__(self):
|
||||
if getattr(builtins, "__in_sphinx__", False):
|
||||
return str(self)
|
||||
return "artiq.compiler.types.TInstance({}, {})".format(
|
||||
repr(self.name), repr(self.attributes))
|
||||
|
||||
@ -474,6 +489,8 @@ class TModule(TMono):
|
||||
self.constant_attributes = set()
|
||||
|
||||
def __repr__(self):
|
||||
if getattr(builtins, "__in_sphinx__", False):
|
||||
return str(self)
|
||||
return "artiq.compiler.types.TModule({}, {})".format(
|
||||
repr(self.name), repr(self.attributes))
|
||||
|
||||
@ -513,6 +530,8 @@ class TValue(Type):
|
||||
return fn(accum, self)
|
||||
|
||||
def __repr__(self):
|
||||
if getattr(builtins, "__in_sphinx__", False):
|
||||
return str(self)
|
||||
return "artiq.compiler.types.TValue(%s)" % repr(self.value)
|
||||
|
||||
def __eq__(self, other):
|
||||
@ -571,6 +590,8 @@ class TDelay(Type):
|
||||
return not (self == other)
|
||||
|
||||
def __repr__(self):
|
||||
if getattr(builtins, "__in_sphinx__", False):
|
||||
return str(self)
|
||||
if self.duration is None:
|
||||
return "<{}.TIndeterminateDelay>".format(__name__)
|
||||
elif self.cause is None:
|
||||
|
@ -233,7 +233,7 @@ class AD53xx:
|
||||
def write_gain_mu(self, channel, gain=0xffff):
|
||||
"""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.
|
||||
|
||||
:param gain: 16-bit gain register value (default: 0xffff)
|
||||
@ -245,7 +245,7 @@ class AD53xx:
|
||||
def write_offset_mu(self, channel, offset=0x8000):
|
||||
"""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.
|
||||
|
||||
:param offset: 16-bit offset register value (default: 0x8000)
|
||||
@ -258,7 +258,7 @@ class AD53xx:
|
||||
"""Program the DAC offset voltage for a channel.
|
||||
|
||||
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.
|
||||
|
||||
:param voltage: the offset voltage
|
||||
@ -270,7 +270,7 @@ class AD53xx:
|
||||
def write_dac_mu(self, channel, value):
|
||||
"""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.
|
||||
"""
|
||||
self.bus.write(
|
||||
@ -280,7 +280,7 @@ class AD53xx:
|
||||
def write_dac(self, channel, voltage):
|
||||
"""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.
|
||||
"""
|
||||
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.
|
||||
|
||||
See :meth load:.
|
||||
See :meth:`load`.
|
||||
|
||||
:param values: list of DAC values to program
|
||||
:param channels: list of DAC channels to program. If not specified,
|
||||
@ -355,7 +355,7 @@ class AD53xx:
|
||||
""" Two-point calibration of a DAC channel.
|
||||
|
||||
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
|
||||
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
|
||||
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,
|
||||
pll_n=40, pll_cp=7, pll_vco=5, sync_delay_seed=-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.core = self.cpld.core
|
||||
self.bus = self.cpld.bus
|
||||
|
@ -26,10 +26,11 @@ class AD9912:
|
||||
is the reference clock divider (both set in the parent Urukul CPLD
|
||||
instance).
|
||||
"""
|
||||
kernel_invariants = {"chip_select", "cpld", "core", "bus", "ftw_per_hz"}
|
||||
|
||||
def __init__(self, dmgr, chip_select, cpld_device, sw_device=None,
|
||||
pll_n=10):
|
||||
self.kernel_invariants = {"cpld", "core", "bus", "chip_select",
|
||||
"pll_n", "ftw_per_hz"}
|
||||
self.cpld = dmgr.get(cpld_device)
|
||||
self.core = self.cpld.core
|
||||
self.bus = self.cpld.bus
|
||||
|
@ -102,15 +102,15 @@ def decode_dump(data):
|
||||
# messages are big endian
|
||||
parts = struct.unpack(endian + "IQbbb", data[:15])
|
||||
(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
|
||||
if expected_len != len(data):
|
||||
raise ValueError("analyzer dump has incorrect length "
|
||||
"(got {}, expected {})".format(
|
||||
len(data), expected_len))
|
||||
if error_occured:
|
||||
logger.warning("error occured within the analyzer, "
|
||||
if error_occurred:
|
||||
logger.warning("error occurred within the analyzer, "
|
||||
"data may be corrupted")
|
||||
if total_byte_count > sent_bytes:
|
||||
logger.info("analyzer ring buffer has wrapped %d times",
|
||||
|
@ -66,13 +66,13 @@ def _receive_list(kernel, embedding_map):
|
||||
tag = chr(kernel._read_int8())
|
||||
if tag == "b":
|
||||
buffer = kernel._read(length)
|
||||
return list(buffer)
|
||||
return list(struct.unpack(kernel.endian + "%s?" % length, buffer))
|
||||
elif tag == "i":
|
||||
buffer = kernel._read(4 * length)
|
||||
return list(struct.unpack(kernel.endian + "%sl" % length, buffer))
|
||||
elif tag == "I":
|
||||
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":
|
||||
buffer = kernel._read(8 * length)
|
||||
return list(struct.unpack(kernel.endian + "%sd" % length, buffer))
|
||||
@ -96,7 +96,7 @@ def _receive_array(kernel, embedding_map):
|
||||
length = numpy.prod(shape)
|
||||
if tag == "b":
|
||||
buffer = kernel._read(length)
|
||||
elems = numpy.ndarray((length, ), 'B', buffer)
|
||||
elems = numpy.ndarray((length, ), '?', buffer)
|
||||
elif tag == "i":
|
||||
buffer = kernel._read(4 * length)
|
||||
elems = numpy.ndarray((length, ), kernel.endian + 'i4', buffer)
|
||||
@ -437,12 +437,12 @@ class CommKernel:
|
||||
self._write_bool(value)
|
||||
elif tag == "i":
|
||||
check(isinstance(value, (int, numpy.int32)) and
|
||||
(-2**31 < value < 2**31-1),
|
||||
(-2**31 <= value < 2**31),
|
||||
lambda: "32-bit int")
|
||||
self._write_int32(value)
|
||||
elif tag == "I":
|
||||
check(isinstance(value, (int, numpy.int32, numpy.int64)) and
|
||||
(-2**63 < value < 2**63-1),
|
||||
(-2**63 <= value < 2**63),
|
||||
lambda: "64-bit int")
|
||||
self._write_int64(value)
|
||||
elif tag == "f":
|
||||
@ -451,8 +451,8 @@ class CommKernel:
|
||||
self._write_float64(value)
|
||||
elif tag == "F":
|
||||
check(isinstance(value, Fraction) and
|
||||
(-2**63 < value.numerator < 2**63-1) and
|
||||
(-2**63 < value.denominator < 2**63-1),
|
||||
(-2**63 <= value.numerator < 2**63) and
|
||||
(-2**63 <= value.denominator < 2**63),
|
||||
lambda: "64-bit Fraction")
|
||||
self._write_int64(value.numerator)
|
||||
self._write_int64(value.denominator)
|
||||
@ -476,11 +476,19 @@ class CommKernel:
|
||||
if tag_element == "b":
|
||||
self._write(bytes(value))
|
||||
elif tag_element == "i":
|
||||
self._write(struct.pack(self.endian + "%sl" %
|
||||
len(value), *value))
|
||||
try:
|
||||
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":
|
||||
self._write(struct.pack(self.endian + "%sq" %
|
||||
len(value), *value))
|
||||
try:
|
||||
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":
|
||||
self._write(struct.pack(self.endian + "%sd" %
|
||||
len(value), *value))
|
||||
@ -555,14 +563,6 @@ class CommKernel:
|
||||
|
||||
try:
|
||||
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:
|
||||
raise
|
||||
except Exception as exn:
|
||||
@ -609,6 +609,14 @@ class CommKernel:
|
||||
self._write_int32(-1) # column not known
|
||||
self._write_string(function)
|
||||
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):
|
||||
name = self._read_string()
|
||||
|
@ -176,7 +176,7 @@ class CommMgmt:
|
||||
self._write_bytes(value)
|
||||
ty = self._read_header()
|
||||
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:
|
||||
raise IOError("Incorrect reply from device: {} (expected {})".
|
||||
format(ty, Reply.Success))
|
||||
|
@ -82,12 +82,12 @@ class CommMonInj:
|
||||
if not ty:
|
||||
return
|
||||
if ty == b"\x00":
|
||||
payload = await self._reader.read(9)
|
||||
payload = await self._reader.readexactly(9)
|
||||
channel, probe, value = struct.unpack(
|
||||
self.endian + "lbl", payload)
|
||||
self.monitor_cb(channel, probe, value)
|
||||
elif ty == b"\x01":
|
||||
payload = await self._reader.read(6)
|
||||
payload = await self._reader.readexactly(6)
|
||||
channel, override, value = struct.unpack(
|
||||
self.endian + "lbb", payload)
|
||||
self.injection_status_cb(channel, override, value)
|
||||
|
@ -175,24 +175,24 @@
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"refclk": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"clk_sel": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 3
|
||||
},
|
||||
"clk_div": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 3
|
||||
},
|
||||
"pll_n": {
|
||||
"refclk": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"clk_sel": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 3
|
||||
},
|
||||
"clk_div": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 3
|
||||
},
|
||||
"pll_n": {
|
||||
"type": "integer"
|
||||
},
|
||||
"pll_vco": {
|
||||
"pll_vco": {
|
||||
"type": "integer"
|
||||
},
|
||||
"dds": {
|
||||
@ -282,20 +282,20 @@
|
||||
"minItems": 2,
|
||||
"maxItems": 2
|
||||
},
|
||||
"refclk": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"clk_sel": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 3
|
||||
},
|
||||
"pll_n": {
|
||||
"refclk": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"clk_sel": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 3
|
||||
},
|
||||
"pll_n": {
|
||||
"type": "integer",
|
||||
"default": 32
|
||||
},
|
||||
"pll_vco": {
|
||||
"pll_vco": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
@ -364,17 +364,25 @@
|
||||
"minItems": 1,
|
||||
"maxItems": 1
|
||||
},
|
||||
"refclk": {
|
||||
"type": "number",
|
||||
"exclusiveMinimum": 0,
|
||||
"refclk": {
|
||||
"type": "number",
|
||||
"exclusiveMinimum": 0,
|
||||
"default": 100e6
|
||||
},
|
||||
"clk_sel": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 3,
|
||||
},
|
||||
"clk_sel": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 3
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": ["xo", "mmcx", "sma"]
|
||||
}
|
||||
],
|
||||
"default": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["ports"]
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""RTIO driver for the Fastino 32channel, 16 bit, 2.5 MS/s per channel,
|
||||
streaming DAC.
|
||||
"""
|
||||
from numpy import int32
|
||||
|
||||
from artiq.language.core import kernel, portable, delay
|
||||
from artiq.coredevice.rtio import (rtio_output, rtio_output_wide,
|
||||
@ -112,7 +113,7 @@ class Fastino:
|
||||
:param voltage: Voltage in SI Volts.
|
||||
: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:
|
||||
raise ValueError("DAC voltage out of bounds")
|
||||
return data
|
||||
@ -129,7 +130,7 @@ class Fastino:
|
||||
v = self.voltage_to_mu(voltage[i])
|
||||
if i & 1:
|
||||
v = data[i // 2] | (v << 16)
|
||||
data[i // 2] = v
|
||||
data[i // 2] = int32(v)
|
||||
|
||||
@kernel
|
||||
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_TERM0`: ADC channel 0 termination indicator
|
||||
* :const:`PHASER_STA_TERM1`: ADC channel 1 termination indicator
|
||||
* :const:`PHASER_STA_SPI_IDLE`: SPI machine is idle and data registers
|
||||
can be read/written
|
||||
* :const:`PHASER_STA_SPI_IDLE`: SPI machine is idle and data registers can be read/written
|
||||
|
||||
:return: Status register
|
||||
"""
|
||||
@ -566,7 +565,7 @@ class Phaser:
|
||||
def dac_iotest(self, pattern) -> TInt32:
|
||||
"""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)
|
||||
"""
|
||||
if len(pattern) != 4:
|
||||
@ -656,10 +655,11 @@ class PhaserChannel:
|
||||
* multiple oscillators (in the coredevice phy),
|
||||
* an interpolation chain and digital upconverter (DUC) on Phaser,
|
||||
* several channel-specific settings in the DAC:
|
||||
|
||||
* quadrature modulation compensation QMC
|
||||
* 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.
|
||||
|
||||
Attributes:
|
||||
|
@ -45,6 +45,7 @@ class AppletsCCBDock(applets.AppletsDock):
|
||||
self.ccbp_group_action.setMenu(ccbp_group_menu)
|
||||
self.table.addAction(self.ccbp_group_action)
|
||||
self.table.itemSelectionChanged.connect(self.update_group_ccbp_menu)
|
||||
self.update_group_ccbp_menu()
|
||||
|
||||
ccbp_global_menu = QtWidgets.QMenu()
|
||||
actiongroup = QtWidgets.QActionGroup(self.table)
|
||||
|
@ -258,7 +258,7 @@ class _ExperimentDock(QtWidgets.QMdiSubWindow):
|
||||
datetime.setDate(QtCore.QDate.currentDate())
|
||||
else:
|
||||
datetime.setDateTime(QtCore.QDateTime.fromMSecsSinceEpoch(
|
||||
scheduling["due_date"]*1000))
|
||||
int(scheduling["due_date"]*1000)))
|
||||
datetime_en.setChecked(scheduling["due_date"] is not None)
|
||||
|
||||
def update_datetime(dt):
|
||||
|
@ -226,7 +226,7 @@ def setup_from_ddb(ddb):
|
||||
dds_sysclk = v["arguments"]["sysclk"]
|
||||
widget = _WidgetDesc(k, comment, _DDSWidget, (bus_channel, channel, k))
|
||||
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")):
|
||||
spi_device = v["arguments"]["spi_device"]
|
||||
spi_device = ddb[spi_device]
|
||||
|
@ -5,7 +5,7 @@ device_db = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.core",
|
||||
"class": "Core",
|
||||
"arguments": {"host": core_addr, "ref_period": 1/(8*150e6)}
|
||||
"arguments": {"host": core_addr, "ref_period": 1/(8*125e6)}
|
||||
},
|
||||
"core_log": {
|
||||
"type": "controller",
|
||||
|
@ -5,7 +5,7 @@ device_db = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.core",
|
||||
"class": "Core",
|
||||
"arguments": {"host": core_addr, "ref_period": 1/(8*150e6)}
|
||||
"arguments": {"host": core_addr, "ref_period": 1/(8*125e6)}
|
||||
},
|
||||
"core_log": {
|
||||
"type": "controller",
|
||||
@ -69,7 +69,7 @@ device_db.update(
|
||||
"arguments": {
|
||||
"spi_device": "spi_urukul0",
|
||||
"io_update_device": "ttl_urukul0_io_update",
|
||||
"refclk": 150e6,
|
||||
"refclk": 125e6,
|
||||
"clk_sel": 2
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ device_db = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.core",
|
||||
"class": "Core",
|
||||
"arguments": {"host": core_addr, "ref_period": 1/(8*150e6)}
|
||||
"arguments": {"host": core_addr, "ref_period": 1/(8*125e6)}
|
||||
},
|
||||
"core_log": {
|
||||
"type": "controller",
|
||||
|
@ -5,7 +5,7 @@ device_db = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.core",
|
||||
"class": "Core",
|
||||
"arguments": {"host": core_addr, "ref_period": 1/(8*150e6)}
|
||||
"arguments": {"host": core_addr, "ref_period": 1/(8*125e6)}
|
||||
},
|
||||
"core_log": {
|
||||
"type": "controller",
|
||||
|
@ -34,9 +34,9 @@ fn read(addr: u16) -> u8 {
|
||||
}
|
||||
|
||||
// ad9154 mode 1
|
||||
// linerate 5Gbps or 6Gbps
|
||||
// deviceclock_fpga 125MHz or 150MHz
|
||||
// deviceclock_dac 500MHz or 600MHz
|
||||
// linerate 10Gbps
|
||||
// deviceclock_fpga 125MHz
|
||||
// deviceclock_dac 1000MHz
|
||||
|
||||
struct JESDSettings {
|
||||
did: u8,
|
||||
@ -87,7 +87,7 @@ const JESD_SETTINGS: JESDSettings = JESDSettings {
|
||||
np: 16,
|
||||
f: 2,
|
||||
s: 2,
|
||||
k: 16,
|
||||
k: 32,
|
||||
cs: 0,
|
||||
|
||||
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_2, 0x01); // magic
|
||||
|
||||
write(ad9154_reg::SPI_PAGEINDX, 0x3); // A and B dual
|
||||
|
||||
write(ad9154_reg::INTERP_MODE, 0x03); // 4x
|
||||
write(ad9154_reg::INTERP_MODE, 0x01); // 2x
|
||||
write(ad9154_reg::MIX_MODE, 0);
|
||||
write(ad9154_reg::DATA_FORMAT, 0*ad9154_reg::BINARY_FORMAT); // s16
|
||||
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);
|
||||
|
||||
write(ad9154_reg::GENERAL_JRX_CTRL_1, 1); // subclass 1
|
||||
write(ad9154_reg::LMFC_DELAY_0, 0);
|
||||
write(ad9154_reg::LMFC_DELAY_1, 0);
|
||||
write(ad9154_reg::LMFC_VAR_0, 0x0a); // receive buffer delay
|
||||
write(ad9154_reg::LMFC_VAR_1, 0x0a);
|
||||
// LMFCDel & LMFCVar were deduced from values of DYN_LINK_LATENCY_0
|
||||
// gathered from repeated power-cycles; see datasheet (Rev. C) p.44
|
||||
// "Link Delay Setup Example, Without Known Delay"
|
||||
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
|
||||
// datasheet seems to say ENABLE and ARM should be separate steps,
|
||||
// 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,
|
||||
0x1*ad9154_reg::SYNCMODE | 1*ad9154_reg::SYNCENABLE |
|
||||
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,
|
||||
0x1*ad9154_reg::LINK_EN | 0*ad9154_reg::LINK_PAGE |
|
||||
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");
|
||||
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> {
|
||||
spi_setup(dacno);
|
||||
|
||||
write(ad9154_reg::SPI_PAGEINDX, 0x3); // A and B dual
|
||||
write(ad9154_reg::SYNC_CONTROL,
|
||||
0x1*ad9154_reg::SYNCMODE | 1*ad9154_reg::SYNCENABLE |
|
||||
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");
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
@ -149,20 +149,20 @@ pub mod hmc7043 {
|
||||
|
||||
// enabled, divider, output config, is sysref
|
||||
const OUTPUT_CONFIG: [(bool, u16, u8, bool); 14] = [
|
||||
(true, DAC_CLK_DIV, 0x08, false), // 0: DAC1_CLK
|
||||
(true, SYSREF_DIV, 0x01, true), // 1: DAC1_SYSREF
|
||||
(true, DAC_CLK_DIV, 0x08, false), // 2: DAC0_CLK
|
||||
(true, SYSREF_DIV, 0x01, true), // 3: DAC0_SYSREF
|
||||
(true, SYSREF_DIV, 0x10, true), // 4: AMC_FPGA_SYSREF0
|
||||
(false, FPGA_CLK_DIV, 0x10, true), // 5: AMC_FPGA_SYSREF1
|
||||
(false, 0, 0x10, false), // 6: unused
|
||||
(true, FPGA_CLK_DIV, 0x10, true), // 7: RTM_FPGA_SYSREF0
|
||||
(true, FPGA_CLK_DIV, 0x08, false), // 8: GTP_CLK0_IN
|
||||
(false, 0, 0x10, false), // 9: unused
|
||||
(false, 0, 0x10, false), // 10: unused
|
||||
(false, 0, 0x08, false), // 11: unused / uFL
|
||||
(false, 0, 0x10, false), // 12: unused
|
||||
(false, FPGA_CLK_DIV, 0x10, true), // 13: RTM_FPGA_SYSREF1
|
||||
(true, DAC_CLK_DIV, 0x08, false), // 0: DAC1_CLK
|
||||
(true, SYSREF_DIV, 0x01, true), // 1: DAC1_SYSREF
|
||||
(true, DAC_CLK_DIV, 0x08, false), // 2: DAC0_CLK
|
||||
(true, SYSREF_DIV, 0x01, true), // 3: DAC0_SYSREF
|
||||
(true, SYSREF_DIV, 0x10, true), // 4: AMC_FPGA_SYSREF0
|
||||
(false, FPGA_CLK_DIV, 0x10, true), // 5: AMC_FPGA_SYSREF1
|
||||
(false, 0, 0x10, false), // 6: unused
|
||||
(true, FPGA_CLK_DIV, 0x10, true), // 7: RTM_FPGA_SYSREF0
|
||||
(true, FPGA_CLK_DIV/2, 0x08, false), // 8: GTP_CLK0_IN
|
||||
(false, 0, 0x10, false), // 9: unused
|
||||
(false, 0, 0x10, false), // 10: unused
|
||||
(false, 0, 0x08, false), // 11: unused / uFL
|
||||
(false, 0, 0x10, false), // 12: unused
|
||||
(false, FPGA_CLK_DIV, 0x10, true), // 13: RTM_FPGA_SYSREF1
|
||||
];
|
||||
|
||||
fn spi_setup() {
|
||||
@ -393,8 +393,6 @@ pub mod hmc7043 {
|
||||
pub fn init() -> Result<(), &'static str> {
|
||||
#[cfg(all(hmc830_ref = "125", rtio_frequency = "125.0"))]
|
||||
const DIV: (u32, u32, u32, u32) = (2, 32, 0, 1); // 125MHz -> 2.0GHz
|
||||
#[cfg(all(hmc830_ref = "150", rtio_frequency = "150.0"))]
|
||||
const DIV: (u32, u32, u32, u32) = (2, 32, 0, 1); // 150MHz -> 2.4GHz
|
||||
|
||||
/* do not use other SPI devices before HMC830 SPI mode selection */
|
||||
hmc830::select_spi_mode();
|
||||
@ -406,7 +404,7 @@ pub fn init() -> Result<(), &'static str> {
|
||||
hmc830::check_locked()?;
|
||||
|
||||
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::detect()?;
|
||||
|
@ -26,8 +26,6 @@ pub mod rpc_queue;
|
||||
|
||||
#[cfg(has_si5324)]
|
||||
pub mod si5324;
|
||||
#[cfg(has_wrpll)]
|
||||
pub mod wrpll;
|
||||
|
||||
#[cfg(has_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;
|
||||
#[cfg(has_slave_fpga_cfg)]
|
||||
pub mod slave_fpga;
|
||||
#[cfg(has_mmcspi)]
|
||||
pub mod mmcspi;
|
200
artiq/firmware/libboard_misoc/mmcspi.rs
Normal file
200
artiq/firmware/libboard_misoc/mmcspi.rs
Normal file
@ -0,0 +1,200 @@
|
||||
use super::{csr, clock};
|
||||
|
||||
// Sayma MMC SSP1 configuration:
|
||||
//
|
||||
// References:
|
||||
// (i) Sayma MMC FPGA SPI port initialisation: https://github.com/sinara-hw/openMMC/blob/sayma-devel/modules/fpga_spi.c
|
||||
// (ii) Sayma MMC configuration: https://github.com/sinara-hw/openMMC/blob/sayma-devel/port/ucontroller/nxp/lpc17xx/lpc17_ssp.c::ssp_init()
|
||||
// (iii) openMMC SSP driver: https://github.com/sinara-hw/openMMC/blob/sayma-devel/port/ucontroller/nxp/lpc17xx/lpcopen/src/ssp_17xx_40xx.c)
|
||||
//
|
||||
// * Data Size Select <DSS>: 8-bit transfer (see FPGA_SPI_FRAME_SIZE)
|
||||
// * Frame Format <FRF>: SPI (see lpc17_ssp.c::ssp_init())
|
||||
// * Clock Out Polarity <CPOL>: CPOL=0 (CLK is low when idling) (see lpc17_ssp.c::ssp_init())
|
||||
// * Clock Out Phase <CPHA>: CPHA=0 (data is captured on leading edge) (see lpc17_ssp.c::ssp_init())
|
||||
// * CPOL=0, CPHA=0 ==> data is captured at rising edge
|
||||
// * Clock Frequency: 10000000 == 10 MHz (see FPGA_SPI_BITRATE)
|
||||
|
||||
// TODO: consider making a generic SPI receiver for customisable configuration
|
||||
|
||||
static mut PREV_CS_N: bool = true; // High when idling
|
||||
static mut PREV_CLK: bool = false; // Low when idling
|
||||
|
||||
// List of expected values
|
||||
// openMMC modules/fpga_spi.h
|
||||
const WR_COMMAND: u8 = 0x80;
|
||||
const ADDR_HEADER: u16 = 0x0005; // "Data Valid Byte"
|
||||
const DATA_HEADER: u32 = 0x55555555;
|
||||
const FPGA_UPDATE_RATE: u64 = 5000; // Delay interval between broadcast
|
||||
|
||||
// Layout of MMC-to-FPGA data
|
||||
// (see openMMC modules/fpga_spi.h board_diagnostic_t)
|
||||
// cardID: u32 array of length 4
|
||||
const ADDR_CARD_ID_0: u16 = 0; // cardID[0]: bits[31:24] = EUI48 byte 3
|
||||
// bits[23:16] = EUI48 byte 2 (0x3D)
|
||||
// bits[15: 8] = EUI48 byte 1 (0xC2)
|
||||
// bits[ 7: 0] = EUI48 byte 0 (0xFC)
|
||||
const ADDR_CARD_ID_1: u16 = 1; // cardID[1]: bits[47:40] = EUI48 byte 5
|
||||
// bits[39:32] = EUI48 byte 4
|
||||
const ADDR_SLOT_ID: u16 = 16; // Note: currently unused by FPGA
|
||||
const ADDR_IPMI_ADDR: u16 = 20; // Note: currently unused by FPGA
|
||||
const ADDR_DATA_VALID: u16 = 24; // Note: currently unused by FPGA
|
||||
const ADDR_SENSOR: u16 = 28; // u32 array of length 21; see openMMC modules/sdr.h NUM_SENSOR
|
||||
// Note: currently unused by FPGA
|
||||
const ADDR_FMC_SLOT: u16 = 112; // Note: currently unused by FPGA
|
||||
|
||||
|
||||
fn cs_n() -> bool {
|
||||
unsafe { csr::mmcspi::cs_n_in_read() == 1 }
|
||||
}
|
||||
|
||||
fn detect_cs_n_rise(timeout_us: u64) -> bool {
|
||||
let start = clock::get_us();
|
||||
while clock::get_us() - start < timeout_us {
|
||||
if cs_n() && unsafe { !PREV_CS_N } {
|
||||
unsafe { PREV_CS_N = true; }
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn detect_cs_n_fall(timeout_us: u64) -> bool {
|
||||
let start = clock::get_us();
|
||||
while clock::get_us() - start < timeout_us {
|
||||
if !cs_n() && unsafe { PREV_CS_N } {
|
||||
unsafe { PREV_CS_N = false; }
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn clk() -> bool {
|
||||
unsafe { csr::mmcspi::clk_in_read() == 1 }
|
||||
}
|
||||
|
||||
fn detect_clk_rise(timeout_us: u64) -> bool {
|
||||
let start = clock::get_us();
|
||||
while clock::get_us() - start < timeout_us {
|
||||
if clk() && unsafe { !PREV_CLK } {
|
||||
unsafe { PREV_CLK = true; }
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn detect_clk_fall(timeout_us: u64) -> bool {
|
||||
let start = clock::get_us();
|
||||
while clock::get_us() - start < timeout_us {
|
||||
if !clk() && unsafe { PREV_CLK } {
|
||||
unsafe { PREV_CLK = false; }
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn mosi() -> u8 {
|
||||
unsafe { csr::mmcspi::mosi_in_read() & 1 }
|
||||
}
|
||||
|
||||
/// Detects CS_n assertion and keeps reading until the buffer is full or CS_n is deasserted
|
||||
/// TODO: Generalise this driver for future possible changes to the MMC SPI master settings
|
||||
fn read_continuous(buf: &mut [u8], timeout_ms: u64) {
|
||||
// Register CS_n and CLK states
|
||||
unsafe {
|
||||
// Give up if CS_n has already been asserted (we're in the middle of transaction)
|
||||
if !cs_n() { return } else { PREV_CS_N = true }
|
||||
PREV_CLK = clk();
|
||||
}
|
||||
|
||||
// Wait until timeout or CS_n falling edge is detected, which indicates a new transaction
|
||||
if !detect_cs_n_fall(timeout_ms * 1000) { return }
|
||||
|
||||
for byte_ind in 0..buf.len() {
|
||||
// Read bits from MSB to LSB
|
||||
for bit_ind in (0..8).rev() {
|
||||
// If CS_n goes high, return to indicate a complete SPI transaction
|
||||
if cs_n() { break }
|
||||
// Detect and register CLK rising edge
|
||||
if !detect_clk_rise(1000) { return }
|
||||
// Store the MOSI state as the current bit of the current byte
|
||||
if mosi() == 1 {
|
||||
buf[byte_ind] |= 1 << bit_ind;
|
||||
}
|
||||
// Detect and register CLK falling edge
|
||||
if !detect_clk_fall(1000) { return }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert bytes to u16 (from big-endian)
|
||||
fn to_u16(buf: &[u8]) -> u16 {
|
||||
let mut value = 0_u16;
|
||||
for i in 0..2 {
|
||||
value |= (buf[i] as u16) << ((1-i) * 8);
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
/// Convert bytes to u32 (from big-endian)
|
||||
fn to_u32(buf: &[u8]) -> u32 {
|
||||
let mut value = 0_u32;
|
||||
for i in 0..4 {
|
||||
value |= (buf[i] as u32) << ((3-i) * 8);
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
/// Check if the bytes are the MMC broadcast header
|
||||
fn is_broadcast_header(buf: &[u8]) -> bool {
|
||||
buf.len() == 7 &&
|
||||
buf[0] == WR_COMMAND &&
|
||||
to_u16(&buf[1..3]) == ADDR_HEADER &&
|
||||
to_u32(&buf[3..7]) == DATA_HEADER
|
||||
}
|
||||
|
||||
/// Read the SPI to wait and capture the EUI48, and store it to a u8 array;
|
||||
/// Returns Ok() to indicate if the data is captured
|
||||
pub fn read_eui48(buf: &mut [u8]) -> Result<(), ()> {
|
||||
assert!(buf.len() >= 6);
|
||||
let mut spi_buf = [0_u8; 21];
|
||||
let mut is_broadcast = false;
|
||||
|
||||
// Loop 10s to read a continuous byte transaction until the header correspond to the MMC broadcast format
|
||||
let start = clock::get_ms();
|
||||
while !is_broadcast && clock::get_ms() - start <= 10_000 {
|
||||
// Read 21 contiguous bytes in a row, which is broadcast every 5 seconds
|
||||
read_continuous(&mut spi_buf, FPGA_UPDATE_RATE + 100); // +100ms margin
|
||||
// Check the header
|
||||
is_broadcast = is_broadcast_header(&spi_buf[0..7]);
|
||||
}
|
||||
// Return Err(()) if no broadcast header is detected
|
||||
if !is_broadcast { return Err(()) }
|
||||
// Truncate the header to get all data captured
|
||||
let data = &spi_buf[7..];
|
||||
|
||||
let (mut eui48_lo_ok, mut eui48_hi_ok) = (false, false);
|
||||
for i in 0..data.len()/7 {
|
||||
match to_u16(&data[i*7+1..i*7+3]) {
|
||||
// EUI48[31:0], big-endian
|
||||
ADDR_CARD_ID_0 => {
|
||||
for j in 0..4 { buf[j] = data[i*7 + 6-j] }
|
||||
eui48_lo_ok = true;
|
||||
}
|
||||
// EUI48[47:32], big-endian
|
||||
ADDR_CARD_ID_1 => {
|
||||
for j in 0..2 { buf[4 + j] = data[i*7 + 6-j] }
|
||||
eui48_hi_ok = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
match (eui48_lo_ok, eui48_hi_ok) {
|
||||
(true, true) => Ok(()),
|
||||
// This should never return Err(), unless the broadcast format has changed
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
@ -5,6 +5,8 @@ use smoltcp::wire::{EthernetAddress, IpAddress};
|
||||
use config;
|
||||
#[cfg(soc_platform = "kasli")]
|
||||
use i2c_eeprom;
|
||||
#[cfg(soc_platform = "sayma_amc")]
|
||||
use mmcspi;
|
||||
|
||||
|
||||
pub struct NetAddresses {
|
||||
@ -40,7 +42,11 @@ pub fn get_adresses() -> NetAddresses {
|
||||
.unwrap_or_else(|_e| EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x21]));
|
||||
}
|
||||
#[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")]
|
||||
{ hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x19]); }
|
||||
#[cfg(soc_platform = "kc705")]
|
||||
|
@ -1,5 +1,6 @@
|
||||
#![no_std]
|
||||
#![cfg_attr(feature = "alloc", feature(alloc))]
|
||||
#![feature(extern_prelude)]
|
||||
|
||||
extern crate failure;
|
||||
#[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 => {
|
||||
consume_value!(CMutSlice<u8>, |ptr| {
|
||||
let length = reader.read_u32()? as usize;
|
||||
*ptr = CMutSlice::new(alloc(length)? as *mut u8, length);
|
||||
reader.read_exact((*ptr).as_mut())?;
|
||||
if length > 0 {
|
||||
*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(())
|
||||
})
|
||||
}
|
||||
|
@ -119,19 +119,6 @@ fn setup_si5324_as_synthesizer() {
|
||||
bwsel : 4,
|
||||
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.
|
||||
#[cfg(all(rtio_frequency = "100.0", not(si5324_ext_ref)))]
|
||||
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: u8 = 0x21;
|
||||
pub const DDMTD_INIT: u8 = 0x22;
|
||||
|
||||
|
||||
fn average_2phases(a: i32, b: i32, modulo: i32) -> i32 {
|
||||
|
@ -140,7 +140,7 @@ pub mod jdac {
|
||||
pub mod jesd204sync {
|
||||
use board_misoc::{csr, clock, config};
|
||||
|
||||
use super::jdac;
|
||||
use super::{jdac, jesd};
|
||||
use super::super::jdac_common;
|
||||
|
||||
const HMC7043_ANALOG_DELAY_RANGE: u8 = 24;
|
||||
@ -410,9 +410,23 @@ pub mod jesd204sync {
|
||||
}
|
||||
|
||||
pub fn sysref_auto_rtio_align() -> Result<(), &'static str> {
|
||||
test_ddmtd_stability(true, 4)?;
|
||||
test_ddmtd_stability(false, 1)?;
|
||||
test_slip_ddmtd()?;
|
||||
for i in 0..3 { // Allow resetting DDMTD core 2 times at max
|
||||
let result = {
|
||||
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...");
|
||||
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};
|
||||
#[cfg(has_si5324)]
|
||||
use board_artiq::si5324;
|
||||
#[cfg(has_wrpll)]
|
||||
use board_artiq::wrpll;
|
||||
use board_artiq::{spi, drtioaux};
|
||||
use board_artiq::drtio_routing;
|
||||
#[cfg(has_hmc830_7043)]
|
||||
@ -302,9 +300,7 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
|
||||
#[cfg(has_ad9154)]
|
||||
let (succeeded, retval) = {
|
||||
#[cfg(rtio_frequency = "125.0")]
|
||||
const LINERATE: u64 = 5_000_000_000;
|
||||
#[cfg(rtio_frequency = "150.0")]
|
||||
const LINERATE: u64 = 6_000_000_000;
|
||||
const LINERATE: u64 = 10_000_000_000;
|
||||
match _reqno {
|
||||
jdac_common::INIT => (board_artiq::ad9154::setup(_dacno, LINERATE).is_ok(), 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 => (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))]
|
||||
@ -419,18 +416,14 @@ fn hardware_tick(ts: &mut u64) {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(has_si5324, rtio_frequency = "150.0"))]
|
||||
const SI5324_SETTINGS: si5324::FrequencySettings
|
||||
= si5324::FrequencySettings {
|
||||
n1_hs : 6,
|
||||
nc1_ls : 6,
|
||||
n2_hs : 10,
|
||||
n2_ls : 270,
|
||||
n31 : 75,
|
||||
n32 : 75,
|
||||
bwsel : 4,
|
||||
crystal_ref: true
|
||||
};
|
||||
#[cfg(has_ad9154)]
|
||||
fn init_ddmtd_reset_ad9154() -> Result<(), &'static str> {
|
||||
jdac_common::init_ddmtd()?;
|
||||
for dacno in 0..csr::CONFIG_AD9154_COUNT {
|
||||
board_artiq::ad9154::reset_and_detect(dacno as u8)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(has_si5324, rtio_frequency = "125.0"))]
|
||||
const SI5324_SETTINGS: si5324::FrequencySettings
|
||||
@ -464,17 +457,6 @@ pub extern fn main() -> i32 {
|
||||
io_expander1 = board_misoc::io_expander::IoExpander::new(1);
|
||||
io_expander0.init().expect("I2C I/O expander #0 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
|
||||
io_expander0.set_oe(0, 1 << 1).unwrap();
|
||||
@ -491,8 +473,6 @@ pub extern fn main() -> i32 {
|
||||
|
||||
#[cfg(has_si5324)]
|
||||
si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324");
|
||||
#[cfg(has_wrpll)]
|
||||
wrpll::init();
|
||||
|
||||
unsafe {
|
||||
csr::drtio_transceiver::stable_clkin_write(1);
|
||||
@ -502,8 +482,6 @@ pub extern fn main() -> i32 {
|
||||
unsafe {
|
||||
csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
|
||||
}
|
||||
#[cfg(has_wrpll)]
|
||||
wrpll::diagnostics();
|
||||
init_rtio_crg();
|
||||
|
||||
#[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::calibrate_skew().expect("failed to calibrate skew");
|
||||
}
|
||||
#[cfg(has_wrpll)]
|
||||
wrpll::select_recovered_clock(true);
|
||||
|
||||
drtioaux::reset(0);
|
||||
drtiosat_reset(false);
|
||||
@ -604,7 +580,7 @@ pub extern fn main() -> i32 {
|
||||
if is_up && !was_up {
|
||||
/*
|
||||
* 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 same oscillator.
|
||||
* 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");
|
||||
#[cfg(has_si5324)]
|
||||
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):
|
||||
reader, writer = await asyncio.open_connection(host, 1380)
|
||||
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))
|
||||
await writer.drain()
|
||||
|
||||
while True:
|
||||
length, = struct.unpack(">l", await reader.readexactly(4))
|
||||
length, = struct.unpack(endian + "l", await reader.readexactly(4))
|
||||
log = await reader.readexactly(length)
|
||||
|
||||
for line in log.decode("utf-8").splitlines():
|
||||
|
@ -11,9 +11,14 @@ from artiq.coredevice import jsondesc
|
||||
|
||||
|
||||
def process_header(output, description):
|
||||
if description["target"] != "kasli":
|
||||
if description["target"] not in ("kasli", "kasli_soc"):
|
||||
raise NotImplementedError
|
||||
|
||||
cpu_target = {
|
||||
"kasli": "or1k",
|
||||
"kasli_soc": "cortexa9"
|
||||
}[description["target"]]
|
||||
|
||||
print(textwrap.dedent("""
|
||||
# Autogenerated for the {variant} variant
|
||||
core_addr = "{core_addr}"
|
||||
@ -23,7 +28,7 @@ def process_header(output, description):
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.core",
|
||||
"class": "Core",
|
||||
"arguments": {{"host": core_addr, "ref_period": {ref_period}}}
|
||||
"arguments": {{"host": core_addr, "ref_period": {ref_period}, "target": "{cpu_target}"}},
|
||||
}},
|
||||
"core_log": {{
|
||||
"type": "controller",
|
||||
@ -58,7 +63,8 @@ def process_header(output, description):
|
||||
""").format(
|
||||
variant=description["variant"],
|
||||
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)
|
||||
|
||||
|
||||
@ -268,6 +274,9 @@ class PeripheralManager:
|
||||
name=mirny_name,
|
||||
mchn=i)
|
||||
|
||||
clk_sel = peripheral["clk_sel"]
|
||||
if isinstance(peripheral["clk_sel"], str):
|
||||
clk_sel = '"' + peripheral["clk_sel"] + '"'
|
||||
self.gen("""
|
||||
device_db["{name}_cpld"] = {{
|
||||
"type": "local",
|
||||
@ -281,7 +290,7 @@ class PeripheralManager:
|
||||
}}""",
|
||||
name=mirny_name,
|
||||
refclk=peripheral["refclk"],
|
||||
clk_sel=peripheral["clk_sel"])
|
||||
clk_sel=clk_sel)
|
||||
|
||||
return next(channel)
|
||||
|
||||
@ -510,7 +519,7 @@ class PeripheralManager:
|
||||
processor = getattr(self, "process_"+str(peripheral["type"]))
|
||||
return processor(rtio_offset, peripheral)
|
||||
|
||||
def add_sfp_leds(self, rtio_offset):
|
||||
def add_board_leds(self, rtio_offset):
|
||||
for i in range(2):
|
||||
self.gen("""
|
||||
device_db["{name}"] = {{
|
||||
@ -541,8 +550,8 @@ def process(output, master_description, satellites):
|
||||
for peripheral in master_description["peripherals"]:
|
||||
n_channels = pm.process(rtio_offset, peripheral)
|
||||
rtio_offset += n_channels
|
||||
if base == "standalone" and master_description["hw_rev"] in ("v1.0", "v1.1"):
|
||||
n_channels = pm.add_sfp_leds(rtio_offset)
|
||||
if base == "standalone":
|
||||
n_channels = pm.add_board_leds(rtio_offset)
|
||||
rtio_offset += n_channels
|
||||
|
||||
for destination, description in satellites:
|
||||
|
@ -79,23 +79,22 @@ Prerequisites:
|
||||
help="actions to perform, default: flash everything")
|
||||
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():
|
||||
p = ["share", "openocd", "scripts"]
|
||||
if os.name == "nt":
|
||||
p.insert(0, "Library")
|
||||
p = os.path.abspath(os.path.join(
|
||||
os.path.dirname(os.path.realpath(shutil.which("openocd"))),
|
||||
"..", *p))
|
||||
return p
|
||||
return os.path.abspath(os.path.join(openocd_root(), *p))
|
||||
|
||||
|
||||
def proxy_path():
|
||||
p = ["share", "bscan-spi-bitstreams"]
|
||||
p = os.path.abspath(os.path.join(
|
||||
os.path.dirname(os.path.realpath(shutil.which("openocd"))),
|
||||
"..", *p))
|
||||
return p
|
||||
return os.path.abspath(os.path.join(openocd_root(), "share", "bscan-spi-bitstreams"))
|
||||
|
||||
|
||||
def find_proxy_bitfile(filename):
|
||||
@ -441,7 +440,7 @@ def main():
|
||||
storage_img = args.storage
|
||||
programmer.write_binary(*config["storage"], storage_img)
|
||||
elif action == "firmware":
|
||||
if variant.endswith("satellite"):
|
||||
if "satellite" in variant:
|
||||
firmware = "satman"
|
||||
else:
|
||||
firmware = "runtime"
|
||||
|
@ -21,7 +21,6 @@ from artiq.master.databases import DeviceDB, DatasetDB
|
||||
from artiq.master.worker_db import DeviceManager, DatasetManager
|
||||
from artiq.coredevice.core import CompileError, host_only
|
||||
from artiq.compiler.embedding import EmbeddingMap
|
||||
from artiq.compiler.targets import OR1KTarget
|
||||
from artiq.compiler import import_cache
|
||||
from artiq.tools import *
|
||||
|
||||
@ -53,7 +52,7 @@ class FileRunner(EnvExperiment):
|
||||
def build(self, file):
|
||||
self.setattr_device("core")
|
||||
self.file = file
|
||||
self.target = OR1KTarget()
|
||||
self.target = self.core.target_cls()
|
||||
|
||||
def run(self):
|
||||
kernel_library = self.compile()
|
||||
|
@ -101,8 +101,9 @@ class SinaraTester(EnvExperiment):
|
||||
sw_device = desc["arguments"]["sw_device"]
|
||||
del self.ttl_outs[sw_device]
|
||||
elif (module, cls) == ("artiq.coredevice.urukul", "CPLD"):
|
||||
io_update_device = desc["arguments"]["io_update_device"]
|
||||
del self.ttl_outs[io_update_device]
|
||||
io_update_device = desc["arguments"].get("io_update_device", None)
|
||||
if io_update_device is not None:
|
||||
del self.ttl_outs[io_update_device]
|
||||
elif (module, cls) == ("artiq.coredevice.sampler", "Sampler"):
|
||||
cnv_device = desc["arguments"]["cnv_device"]
|
||||
del self.ttl_outs[cnv_device]
|
||||
@ -296,6 +297,7 @@ class SinaraTester(EnvExperiment):
|
||||
@kernel
|
||||
def setup_mirny(self, channel, frequency):
|
||||
self.core.break_realtime()
|
||||
delay(1*ms)
|
||||
channel.init()
|
||||
|
||||
channel.set_att_mu(160)
|
||||
|
@ -4,20 +4,20 @@ from migen.genlib.cdc import MultiReg, PulseSynchronizer
|
||||
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.
|
||||
|
||||
class SiPhaser7Series(Module, AutoCSR):
|
||||
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.phase_shift = CSR()
|
||||
self.phase_shift_done = CSRStatus(reset=1)
|
||||
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,
|
||||
# we do not use the crystal reference so that the PFD (f3) frequency
|
||||
# can be high.
|
||||
@ -43,8 +43,8 @@ class SiPhaser7Series(Module, AutoCSR):
|
||||
else:
|
||||
mmcm_freerun_output = mmcm_freerun_output_raw
|
||||
|
||||
# 125MHz/150MHz to 125MHz/150MHz with controllable phase shift,
|
||||
# VCO @ 1000MHz/1200MHz.
|
||||
# 125MHz to 125MHz with controllable phase shift,
|
||||
# VCO @ 1000MHz.
|
||||
# Inserted between CDR and output to Si, used to correct
|
||||
# non-determinstic skew of Si5324.
|
||||
mmcm_ps_fb = Signal()
|
||||
|
@ -77,8 +77,6 @@ class GTHSingle(Module):
|
||||
p_ALIGN_PCOMMA_DET ="FALSE",
|
||||
p_ALIGN_PCOMMA_VALUE =0b0101111100,
|
||||
p_A_RXOSCALRESET =0b0,
|
||||
p_A_RXPROGDIVRESET =0b0,
|
||||
p_A_TXPROGDIVRESET =0b0,
|
||||
p_CBCC_DATA_SOURCE_SEL ="ENCODED",
|
||||
p_CDR_SWAP_MODE_EN =0b0,
|
||||
p_CHAN_BOND_KEEP_ALIGN ="FALSE",
|
||||
@ -314,7 +312,7 @@ class GTHSingle(Module):
|
||||
p_RX_BIAS_CFG0 =0b0000101010110100,
|
||||
p_RX_BUFFER_CFG =0b000000,
|
||||
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_CLK_SLIP_OVRD =0b00000,
|
||||
p_RX_CM_BUF_CFG =0b1010,
|
||||
@ -413,7 +411,7 @@ class GTHSingle(Module):
|
||||
p_TXSYNC_MULTILANE =0 if mode == "single" else 1,
|
||||
p_TXSYNC_OVRD =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_DATA_WIDTH =dw,
|
||||
p_TX_DCD_CFG =0b000010,
|
||||
@ -475,6 +473,7 @@ class GTHSingle(Module):
|
||||
|
||||
# TX Startup/Reset
|
||||
i_GTTXRESET=tx_init.gtXxreset,
|
||||
i_TXPROGDIVRESET=tx_init.gtXxprogdivreset,
|
||||
o_TXRESETDONE=tx_init.Xxresetdone,
|
||||
i_TXDLYSRESET=tx_init.Xxdlysreset if mode != "slave" else self.txdlysreset,
|
||||
o_TXDLYSRESETDONE=tx_init.Xxdlysresetdone,
|
||||
@ -501,6 +500,7 @@ class GTHSingle(Module):
|
||||
|
||||
# RX Startup/Reset
|
||||
i_GTRXRESET=rx_init.gtXxreset,
|
||||
i_RXPROGDIVRESET=rx_init.gtXxprogdivreset,
|
||||
o_RXRESETDONE=rx_init.Xxresetdone,
|
||||
i_RXDLYSRESET=rx_init.Xxdlysreset,
|
||||
o_RXPHALIGNDONE=rxphaligndone,
|
||||
|
@ -18,6 +18,8 @@ class GTHInit(Module):
|
||||
self.plllock = Signal()
|
||||
self.pllreset = Signal()
|
||||
self.gtXxreset = Signal()
|
||||
# Reset signal for programmable divider: https://www.xilinx.com/support/answers/64103.html
|
||||
self.gtXxprogdivreset = Signal()
|
||||
self.Xxresetdone = Signal()
|
||||
self.Xxdlysreset = Signal()
|
||||
self.Xxdlysresetdone = Signal()
|
||||
@ -46,10 +48,12 @@ class GTHInit(Module):
|
||||
|
||||
# Deglitch FSM outputs driving transceiver asynch inputs
|
||||
gtXxreset = Signal()
|
||||
gtXxprogdivreset = Signal()
|
||||
Xxdlysreset = Signal()
|
||||
Xxuserrdy = Signal()
|
||||
self.sync += [
|
||||
self.gtXxreset.eq(gtXxreset),
|
||||
self.gtXxprogdivreset.eq(gtXxprogdivreset),
|
||||
self.Xxdlysreset.eq(Xxdlysreset),
|
||||
self.Xxuserrdy.eq(Xxuserrdy)
|
||||
]
|
||||
@ -80,6 +84,7 @@ class GTHInit(Module):
|
||||
|
||||
startup_fsm.act("RESET_ALL",
|
||||
gtXxreset.eq(1),
|
||||
gtXxprogdivreset.eq(1),
|
||||
self.pllreset.eq(1),
|
||||
pll_reset_timer.wait.eq(1),
|
||||
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 = [
|
||||
("sampler{}_adc_spi_p".format(eem), 0,
|
||||
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),
|
||||
),
|
||||
("sampler{}_adc_spi_n".format(eem), 0,
|
||||
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),
|
||||
),
|
||||
("sampler{}_pgia_spi_p".format(eem), 0,
|
||||
Subsignal("clk", Pins(_eem_pin(eem, 4, "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"))),
|
||||
iostandard(eem),
|
||||
),
|
||||
("sampler{}_pgia_spi_n".format(eem), 0,
|
||||
Subsignal("clk", Pins(_eem_pin(eem, 4, "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"))),
|
||||
iostandard(eem),
|
||||
),
|
||||
@ -570,14 +570,14 @@ class Mirny(_EEM):
|
||||
("mirny{}_spi_p".format(eem), 0,
|
||||
Subsignal("clk", Pins(_eem_pin(eem, 0, "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"))),
|
||||
iostandard(eem),
|
||||
),
|
||||
("mirny{}_spi_n".format(eem), 0,
|
||||
Subsignal("clk", Pins(_eem_pin(eem, 0, "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"))),
|
||||
iostandard(eem),
|
||||
),
|
||||
|
@ -19,31 +19,27 @@ from jesd204b.core import JESD204BCoreTXControl
|
||||
|
||||
|
||||
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
|
||||
refclk_freq = int(150e6)
|
||||
refclk_freq = int(250e6)
|
||||
fabric_freq = int(125e6)
|
||||
|
||||
def __init__(self, platform, use_rtio_clock=False):
|
||||
def __init__(self, platform):
|
||||
self.jreset = CSRStorage(reset=1)
|
||||
self.refclk = Signal()
|
||||
self.clock_domains.cd_jesd = ClockDomain()
|
||||
|
||||
refclk2 = Signal()
|
||||
refclk_pads = platform.request("dac_refclk", 0)
|
||||
platform.add_period_constraint(refclk_pads.p, 1e9/self.refclk_freq)
|
||||
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,
|
||||
o_O=self.refclk, o_ODIV2=refclk2),
|
||||
AsyncResetSynchronizer(self.cd_jesd, self.jreset.storage),
|
||||
o_O=self.refclk),
|
||||
]
|
||||
|
||||
if use_rtio_clock:
|
||||
self.cd_jesd.clk.attr.add("keep")
|
||||
self.comb += self.cd_jesd.clk.eq(ClockSignal("rtio"))
|
||||
else:
|
||||
self.specials += Instance("BUFG_GT", i_I=refclk2, o_O=self.cd_jesd.clk)
|
||||
self.cd_jesd.clk.attr.add("keep")
|
||||
self.comb += self.cd_jesd.clk.eq(ClockSignal("rtio"))
|
||||
self.specials += AsyncResetSynchronizer(self.cd_jesd, self.jreset.storage)
|
||||
|
||||
|
||||
PhyPads = namedtuple("PhyPads", "txp txn")
|
||||
@ -61,7 +57,7 @@ class UltrascaleTX(Module, AutoCSR):
|
||||
"qpll": JESD204BGTHQuadPLL
|
||||
}[pll_type]
|
||||
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)
|
||||
|
||||
jesd_pads = platform.request("dac_jesd", dac)
|
||||
@ -142,7 +138,7 @@ class UltrascaleTX(Module, AutoCSR):
|
||||
pll, phy.transmitter, **phy_channel_cfg)
|
||||
|
||||
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.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"
|
||||
# by P. Moreira and I. Darwazeh
|
||||
class DDMTD(Module, AutoCSR):
|
||||
def __init__(self, input_pads, rtio_clk_freq=150e6):
|
||||
def __init__(self, input_pads, rtio_clk_freq=125e6):
|
||||
N = 64
|
||||
self.reset = CSRStorage(reset=1)
|
||||
self.locked = CSRStatus()
|
||||
@ -189,7 +185,7 @@ class DDMTD(Module, AutoCSR):
|
||||
i_RST=self.reset.storage,
|
||||
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_DIVCLK_DIVIDE=1,
|
||||
|
||||
|
@ -110,7 +110,7 @@ class RawSlicer(Module):
|
||||
for i in range(out_size)})),
|
||||
If(shift_buf, Case(self.source_consume,
|
||||
{i: buf.eq(buf[i*g:])
|
||||
for i in range(out_size)})),
|
||||
for i in range(out_size + 1)})),
|
||||
]
|
||||
|
||||
fsm = FSM(reset_state="FETCH")
|
||||
|
77
artiq/gateware/rtio/phy/ttl_serdes_nortio.py
Normal file
77
artiq/gateware/rtio/phy/ttl_serdes_nortio.py
Normal file
@ -0,0 +1,77 @@
|
||||
from migen import *
|
||||
from migen.genlib.coding import PriorityEncoder
|
||||
|
||||
from artiq.gateware.rtio import rtlink
|
||||
|
||||
|
||||
def _mk_edges(w, direction):
|
||||
l = [(1 << i) - 1 for i in range(w)]
|
||||
if direction == "rising":
|
||||
l = [((1 << w) - 1) ^ x for x in l]
|
||||
elif direction == "falling":
|
||||
pass
|
||||
else:
|
||||
raise ValueError
|
||||
return l
|
||||
|
||||
|
||||
class _SerdesSquareWaveDriver(Module):
|
||||
def __init__(self, serdes_o, rtio_freq, wave_freq):
|
||||
assert wave_freq <= rtio_freq
|
||||
serdes_width = len(serdes_o)
|
||||
assert serdes_width & (serdes_width-1) == 0 # serdes_width must be 2**n
|
||||
|
||||
edges = Array(_mk_edges(serdes_width, "rising"))
|
||||
edges_n = Array(_mk_edges(serdes_width, "falling"))
|
||||
|
||||
phase_accumulator = Signal(32)
|
||||
tuning_word = int((wave_freq/rtio_freq) * 2**32)
|
||||
|
||||
fine_ts = Signal() # indicates which rtiox period within the
|
||||
# current rtio period should the edge be changed
|
||||
logic_level = Signal(reset=1)
|
||||
logic_level_d = Signal(reset=1)
|
||||
self.comb += [
|
||||
fine_ts.eq(phase_accumulator[-log2_int(serdes_width):]),
|
||||
logic_level.eq(~phase_accumulator[-1]),
|
||||
]
|
||||
# Using CD rio such that RtioInitRequest
|
||||
# resets the phase accumulator and logic level registers
|
||||
# (Refer to :class:`artiq.gateware.rtio.core.Core`)
|
||||
self.sync.rio += [
|
||||
logic_level_d.eq(logic_level),
|
||||
If(~logic_level_d & logic_level,
|
||||
serdes_o.eq(edges[fine_ts]),
|
||||
).Elif(logic_level_d & ~logic_level,
|
||||
serdes_o.eq(edges_n[fine_ts]),
|
||||
).Else(
|
||||
serdes_o.eq(Replicate(logic_level_d, serdes_width)),
|
||||
),
|
||||
phase_accumulator.eq(phase_accumulator + tuning_word),
|
||||
]
|
||||
|
||||
|
||||
SEDRES_DRIVER_TYPES = {
|
||||
"square_wave": _SerdesSquareWaveDriver,
|
||||
}
|
||||
|
||||
|
||||
class Output(Module):
|
||||
def __init__(self, serdes, driver_type, **kwargs):
|
||||
assert driver_type in SEDRES_DRIVER_TYPES.keys()
|
||||
|
||||
# Include an unused, dummy rtlink interface just to consume an RTIO channel
|
||||
self.rtlink = rtlink.Interface(
|
||||
rtlink.OInterface(1, fine_ts_width=log2_int(len(serdes.o))))
|
||||
self.probes = [Signal()]
|
||||
self.overrides = [Signal(), Signal()]
|
||||
|
||||
# # #
|
||||
|
||||
self.submodules += SEDRES_DRIVER_TYPES[driver_type](
|
||||
serdes.o, **kwargs)
|
||||
|
||||
|
||||
class InOut(Module):
|
||||
def __init__(self, serdes):
|
||||
raise NotImplementedError()
|
@ -1,6 +1,6 @@
|
||||
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):
|
||||
@ -99,3 +99,23 @@ class InOut(ttl_serdes_generic.InOut):
|
||||
i_INTERMDISABLE=~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)
|
||||
|
||||
|
||||
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.drtio.transceiver import gtp_7series
|
||||
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 import *
|
||||
from artiq.build_soc import *
|
||||
@ -311,6 +310,9 @@ class MasterBase(MiniSoC, AMPSoC):
|
||||
platform = self.platform
|
||||
|
||||
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)
|
||||
|
||||
i2c = self.platform.request("i2c")
|
||||
@ -445,7 +447,10 @@ class MasterBase(MiniSoC, AMPSoC):
|
||||
self.specials += Instance("IBUFDS_GTE2",
|
||||
i_CEB=self.disable_cdr_clk_ibuf,
|
||||
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:
|
||||
# refclksel=0b001 GTREFCLK0 selected
|
||||
# refclksel=0b010 GTREFCLK1 selected
|
||||
@ -472,7 +477,7 @@ class SatelliteBase(BaseSoC):
|
||||
}
|
||||
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,
|
||||
cpu_type="or1k",
|
||||
sdram_controller_type="minicon",
|
||||
@ -482,6 +487,11 @@ class SatelliteBase(BaseSoC):
|
||||
|
||||
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.attr.add("no_retiming")
|
||||
if self.platform.hw_rev == "v2.0":
|
||||
@ -492,7 +502,10 @@ class SatelliteBase(BaseSoC):
|
||||
self.specials += Instance("IBUFDS_GTE2",
|
||||
i_CEB=disable_cdr_clk_ibuf,
|
||||
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(
|
||||
refclksel=0b001,
|
||||
fbdiv=4,
|
||||
@ -583,35 +596,18 @@ class SatelliteBase(BaseSoC):
|
||||
|
||||
rtio_clk_period = 1e9/rtio_clk_freq
|
||||
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
|
||||
if with_wrpll:
|
||||
self.submodules.wrpll_sampler = DDMTDSamplerGTP(
|
||||
self.drtio_transceiver,
|
||||
platform.request("cdr_clk_clean_fabric"))
|
||||
helper_clk_pads = platform.request("ddmtd_helper_clk")
|
||||
self.submodules.wrpll = WRPLL(
|
||||
helper_clk_pads=helper_clk_pads,
|
||||
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"),
|
||||
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"),
|
||||
ddmtd_inputs=self.wrpll_sampler)
|
||||
self.csr_devices.append("wrpll")
|
||||
# note: do not use self.wrpll.cd_helper.clk; otherwise, vivado craps out with:
|
||||
# critical warning: create_clock attempting to set clock on an unknown port/pin
|
||||
# command: "create_clock -period 7.920000 -waveform {0.000000 3.960000} -name
|
||||
# helper_clk [get_xlnx_outside_genome_inst_pin 20 0]
|
||||
platform.add_period_constraint(helper_clk_pads.p, rtio_clk_period*0.99)
|
||||
platform.add_false_path_constraints(self.crg.cd_sys.clk, helper_clk_pads.p)
|
||||
else:
|
||||
self.submodules.siphaser = SiPhaser7Series(
|
||||
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
|
||||
|
||||
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]
|
||||
platform.add_period_constraint(gtp.txoutclk, rtio_clk_period)
|
||||
@ -619,9 +615,6 @@ class SatelliteBase(BaseSoC):
|
||||
platform.add_false_path_constraints(
|
||||
self.crg.cd_sys.clk,
|
||||
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:]:
|
||||
platform.add_period_constraint(gtp.rxoutclk, rtio_clk_period)
|
||||
platform.add_false_path_constraints(
|
||||
@ -700,14 +693,11 @@ def main():
|
||||
parser.add_argument("-V", "--variant", default="tester",
|
||||
help="variant: {} (default: %(default)s)".format(
|
||||
"/".join(sorted(VARIANTS.keys()))))
|
||||
parser.add_argument("--with-wrpll", default=False, action="store_true")
|
||||
parser.add_argument("--gateware-identifier-str", default=None,
|
||||
help="Override ROM identifier")
|
||||
args = parser.parse_args()
|
||||
|
||||
argdict = dict()
|
||||
if args.with_wrpll:
|
||||
argdict["with_wrpll"] = True
|
||||
argdict["gateware_identifier_str"] = args.gateware_identifier_str
|
||||
|
||||
variant = args.variant.lower()
|
||||
|
@ -46,6 +46,11 @@ class GenericStandalone(StandaloneBase):
|
||||
phy = ttl_simple.Output(sfp_ctl.led)
|
||||
self.submodules += 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["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
|
||||
|
@ -54,7 +54,7 @@ class Master(MiniSoC, AMPSoC):
|
||||
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
|
||||
|
||||
platform = self.platform
|
||||
rtio_clk_freq = 150e6
|
||||
rtio_clk_freq = 125e6
|
||||
|
||||
self.comb += platform.request("input_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.drtio.transceiver import gth_ultrascale
|
||||
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 import *
|
||||
from artiq.build_soc import *
|
||||
@ -54,7 +53,7 @@ class SatelliteBase(MiniSoC):
|
||||
}
|
||||
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,
|
||||
cpu_type="or1k",
|
||||
sdram_controller_type="minicon",
|
||||
@ -68,10 +67,6 @@ class SatelliteBase(MiniSoC):
|
||||
|
||||
platform = self.platform
|
||||
|
||||
if with_wrpll:
|
||||
clock_recout_pads = platform.request("ddmtd_rec_clk")
|
||||
else:
|
||||
clock_recout_pads = None
|
||||
if with_sfp:
|
||||
# Use SFP0 to connect to master (Kasli)
|
||||
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")],
|
||||
sys_clk_freq=self.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.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
|
||||
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
|
||||
if with_wrpll:
|
||||
self.comb += [
|
||||
platform.request("filtered_clk_sel").eq(0),
|
||||
platform.request("ddmtd_main_dcxo_oe").eq(1),
|
||||
platform.request("ddmtd_helper_dcxo_oe").eq(1)
|
||||
]
|
||||
self.submodules.wrpll_sampler = DDMTDSamplerExtFF(
|
||||
platform.request("ddmtd_inputs"))
|
||||
self.submodules.wrpll = WRPLL(
|
||||
helper_clk_pads=platform.request("ddmtd_helper_clk"),
|
||||
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"),
|
||||
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"),
|
||||
ddmtd_inputs=self.wrpll_sampler)
|
||||
self.csr_devices.append("wrpll")
|
||||
platform.add_period_constraint(self.wrpll.cd_helper.clk, rtio_clk_period*0.99)
|
||||
platform.add_false_path_constraints(self.crg.cd_sys.clk, self.wrpll.cd_helper.clk)
|
||||
else:
|
||||
self.comb += platform.request("filtered_clk_sel").eq(1)
|
||||
self.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
|
||||
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]
|
||||
platform.add_period_constraint(gth.txoutclk, rtio_clk_period/2)
|
||||
@ -174,6 +152,22 @@ class SatelliteBase(MiniSoC):
|
||||
self.crg.cd_sys.clk,
|
||||
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):
|
||||
# Only add MonInj core if there is anything to monitor
|
||||
if any([len(c.probes) for c in rtio_channels]):
|
||||
@ -195,11 +189,11 @@ class SatelliteBase(MiniSoC):
|
||||
class JDCGSAWG(Module, AutoCSR):
|
||||
def __init__(self, platform, sys_crg, jesd_crg, dac):
|
||||
# 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(
|
||||
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):
|
||||
assert len(Cat(ch.o)) == len(conv)
|
||||
@ -209,34 +203,42 @@ class JDCGSAWG(Module, AutoCSR):
|
||||
class JDCGPattern(Module, AutoCSR):
|
||||
def __init__(self, platform, sys_crg, jesd_crg, dac):
|
||||
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 = []
|
||||
|
||||
ramp = Signal(4)
|
||||
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 += [
|
||||
a.eq(Cat(b)) for a, b in zip(
|
||||
self.jesd.core.sink.flatten(), samples)
|
||||
]
|
||||
# ch0: 16-step ramp with big carry toggles
|
||||
for i in range(4):
|
||||
for i in range(8):
|
||||
self.comb += [
|
||||
samples[0][i][-4:].eq(ramp),
|
||||
samples[0][i][:-4].eq(0x7ff if i % 2 else 0x800)
|
||||
]
|
||||
# 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
|
||||
data = [int(round(cos(i/12*2*pi)*((1 << 15) - 1)))
|
||||
for i in range(12)]
|
||||
k = Signal(2)
|
||||
self.sync.rtio += If(k == 2, k.eq(0)).Else(k.eq(k + 1))
|
||||
data = [int(round(cos(i/20*2*pi)*((1 << 15) - 1)))
|
||||
for i in range(40)]
|
||||
k = Signal(max=5)
|
||||
self.sync.rtio += If(k == 4, k.eq(0)).Else(k.eq(k + 1))
|
||||
self.comb += [
|
||||
Case(k, {
|
||||
i: [samples[1][j].eq(data[i*4 + j]) for j in range(4)]
|
||||
for i in range(3)
|
||||
i: [samples[1][j].eq(data[i*8 + j]) for j in range(8)]
|
||||
for i in range(5)
|
||||
})
|
||||
]
|
||||
# ch2: ch0, ch3: ch1
|
||||
@ -249,13 +251,13 @@ class JDCGPattern(Module, AutoCSR):
|
||||
class JDCGSyncDDS(Module, AutoCSR):
|
||||
def __init__(self, platform, sys_crg, jesd_crg, dac):
|
||||
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.sawgs = []
|
||||
|
||||
ftw = round(2**len(self.coarse_ts)*9e6/600e6)
|
||||
parallelism = 4
|
||||
ftw = round(2**len(self.coarse_ts)*9e6/1000e6)
|
||||
parallelism = 8
|
||||
|
||||
mul_1 = 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.
|
||||
"""
|
||||
def __init__(self, jdcg_type, **kwargs):
|
||||
SatelliteBase.__init__(self, 150e6,
|
||||
identifier_suffix="." + jdcg_type,
|
||||
**kwargs)
|
||||
def __init__(self, jdcg_type, ttlout=False, mcx_sqwave=False, **kwargs):
|
||||
SatelliteBase.__init__(self, identifier_suffix="." + jdcg_type, **kwargs)
|
||||
|
||||
platform = self.platform
|
||||
|
||||
@ -318,19 +318,24 @@ class Satellite(SatelliteBase):
|
||||
phy = ttl_simple.Output(platform.request("user_led", i))
|
||||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
mcx_io = platform.request("mcx_io", 0)
|
||||
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))
|
||||
mcx_io = platform.request("mcx_io", 1)
|
||||
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))
|
||||
for i in range(2):
|
||||
mcx_io = platform.request("mcx_io", i)
|
||||
if mcx_sqwave:
|
||||
phy = ttl_serdes_ultrascale.CustomOutput(4, mcx_io.level,
|
||||
driver_type="square_wave",
|
||||
rtio_freq=self.rtio_clk_freq,
|
||||
wave_freq=9e6)
|
||||
self.comb += mcx_io.direction.eq(1)
|
||||
elif ttlout:
|
||||
phy = ttl_serdes_ultrascale.Output(4, mcx_io.level)
|
||||
self.comb += mcx_io.direction.eq(1)
|
||||
else:
|
||||
phy = ttl_serdes_ultrascale.InOut(4, mcx_io.level)
|
||||
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(
|
||||
platform, use_rtio_clock=True)
|
||||
self.submodules.jesd_crg = jesd204_tools.UltrascaleCRG(platform)
|
||||
cls = {
|
||||
"sawg": JDCGSAWG,
|
||||
"pattern": JDCGPattern,
|
||||
@ -432,7 +437,10 @@ def main():
|
||||
default="sawg",
|
||||
help="Change type of signal generator. This is used exclusively for "
|
||||
"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,
|
||||
help="Override ROM identifier")
|
||||
args = parser.parse_args()
|
||||
@ -442,13 +450,13 @@ def main():
|
||||
soc = Satellite(
|
||||
with_sfp=args.sfp,
|
||||
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,
|
||||
**soc_sayma_amc_argdict(args))
|
||||
elif variant == "simplesatellite":
|
||||
soc = SimpleSatellite(
|
||||
with_sfp=args.sfp,
|
||||
with_wrpll=args.with_wrpll,
|
||||
gateware_identifier_str=args.gateware_identifier_str,
|
||||
**soc_sayma_amc_argdict(args))
|
||||
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.drtio.transceiver import gtp_7series
|
||||
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 import *
|
||||
from artiq.build_soc import add_identifier
|
||||
@ -75,7 +74,7 @@ class _SatelliteBase(BaseSoC):
|
||||
}
|
||||
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,
|
||||
cpu_type="or1k",
|
||||
**kwargs)
|
||||
@ -91,7 +90,10 @@ class _SatelliteBase(BaseSoC):
|
||||
self.specials += Instance("IBUFDS_GTE2",
|
||||
i_CEB=disable_cdrclkc_ibuf,
|
||||
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(
|
||||
refclksel=0b001,
|
||||
fbdiv=4,
|
||||
@ -136,41 +138,23 @@ class _SatelliteBase(BaseSoC):
|
||||
gtp = self.drtio_transceiver.gtps[0]
|
||||
rtio_clk_period = 1e9/rtio_clk_freq
|
||||
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
|
||||
if with_wrpll:
|
||||
self.comb += [
|
||||
platform.request("filtered_clk_sel").eq(0),
|
||||
platform.request("ddmtd_main_dcxo_oe").eq(1),
|
||||
platform.request("ddmtd_helper_dcxo_oe").eq(1)
|
||||
]
|
||||
self.submodules.wrpll_sampler = DDMTDSamplerGTP(
|
||||
self.drtio_transceiver,
|
||||
platform.request("cdr_clk_clean_fabric"))
|
||||
self.submodules.wrpll = WRPLL(
|
||||
helper_clk_pads=platform.request("ddmtd_helper_clk"),
|
||||
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"),
|
||||
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"),
|
||||
ddmtd_inputs=self.wrpll_sampler)
|
||||
self.csr_devices.append("wrpll")
|
||||
platform.add_period_constraint(self.wrpll.cd_helper.clk, rtio_clk_period*0.99)
|
||||
platform.add_false_path_constraints(self.crg.cd_sys.clk, self.wrpll.cd_helper.clk)
|
||||
platform.add_false_path_constraints(self.wrpll.cd_helper.clk, gtp.rxoutclk)
|
||||
else:
|
||||
self.comb += platform.request("filtered_clk_sel").eq(1)
|
||||
self.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
|
||||
|
||||
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.rxoutclk, rtio_clk_period)
|
||||
@ -297,8 +281,7 @@ def main():
|
||||
builder_args(parser)
|
||||
soc_sayma_rtm_args(parser)
|
||||
parser.add_argument("--rtio-clk-freq",
|
||||
default=150, type=int, help="RTIO clock frequency in MHz")
|
||||
parser.add_argument("--with-wrpll", default=False, action="store_true")
|
||||
default=125, type=int, help="RTIO clock frequency in MHz")
|
||||
parser.add_argument("--gateware-identifier-str", default=None,
|
||||
help="Override ROM identifier")
|
||||
parser.set_defaults(output_dir=os.path.join("artiq_sayma", "rtm"))
|
||||
@ -306,7 +289,6 @@ def main():
|
||||
|
||||
soc = Satellite(
|
||||
rtio_clk_freq=1e6*args.rtio_clk_freq,
|
||||
with_wrpll=args.with_wrpll,
|
||||
gateware_identifier_str=args.gateware_identifier_str,
|
||||
**soc_sayma_rtm_argdict(args))
|
||||
builder = SatmanSoCBuilder(soc, **builder_argdict(args))
|
||||
|
@ -67,6 +67,7 @@ def do_dma(dut, address):
|
||||
test_writes1 = [
|
||||
(0x01, 0x23, 0x12, 0x33),
|
||||
(0x901, 0x902, 0x11, 0xeeeeeeeeeeeeeefffffffffffffffffffffffffffffff28888177772736646717738388488),
|
||||
(0x82, 0x289, 0x99, int.from_bytes(b"\xf0" * 64, "little")),
|
||||
(0x81, 0x288, 0x88, 0x8888)
|
||||
]
|
||||
|
||||
|
@ -43,7 +43,7 @@ class TB(Module):
|
||||
)
|
||||
]
|
||||
cnv_old = Signal(reset_less=True)
|
||||
self.sync.async += [
|
||||
self.sync.async_ += [
|
||||
cnv_old.eq(self.cnv),
|
||||
If(Cat(cnv_old, self.cnv) == 0b10,
|
||||
sr.eq(Cat(reversed(self.data[2*i:2*i + 2]))),
|
||||
@ -62,7 +62,7 @@ class TB(Module):
|
||||
def _dly(self, sig, n=0):
|
||||
n += self.params.t_rtt*4//2 # t_{sys,adc,ret}/t_async half rtt
|
||||
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]
|
||||
|
||||
|
||||
@ -85,8 +85,8 @@ def main():
|
||||
assert not (yield dut.done)
|
||||
while not (yield dut.done):
|
||||
yield
|
||||
x = (yield from [(yield d) for d in dut.data])
|
||||
for i, ch in enumerate(x):
|
||||
for i, d in enumerate(dut.data):
|
||||
ch = yield d
|
||||
assert ch == i, (hex(ch), hex(i))
|
||||
|
||||
run_simulation(tb, [run(tb)],
|
||||
@ -95,7 +95,7 @@ def main():
|
||||
"sys": (8, 0),
|
||||
"adc": (8, 0),
|
||||
"ret": (8, 0),
|
||||
"async": (2, 0),
|
||||
"async_": (2, 0),
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -44,8 +44,10 @@ class TB(Module):
|
||||
yield
|
||||
dat = []
|
||||
for dds in self.ddss:
|
||||
v = yield from [(yield getattr(dds, k))
|
||||
for k in "cmd ftw pow asf".split()]
|
||||
v = []
|
||||
for k in "cmd ftw pow asf".split():
|
||||
f = yield getattr(dds, k)
|
||||
v.append(f)
|
||||
dat.append(v)
|
||||
data.append((i, dat))
|
||||
else:
|
||||
|
@ -91,7 +91,7 @@ def main():
|
||||
"sys": (8, 0),
|
||||
"adc": (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:
|
||||
return procdesc["default"]
|
||||
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
|
||||
|
||||
|
||||
@ -416,9 +427,10 @@ class ScanEntry(LayoutWidget):
|
||||
"selected": "NoScan",
|
||||
"NoScan": {"value": 0.0, "repetitions": 1},
|
||||
"RangeScan": {"start": 0.0, "stop": 100.0*scale, "npoints": 10,
|
||||
"randomize": False},
|
||||
"randomize": False, "seed": None},
|
||||
"CenterScan": {"center": 0.*scale, "span": 100.*scale,
|
||||
"step": 10.*scale, "randomize": False},
|
||||
"step": 10.*scale, "randomize": False,
|
||||
"seed": None},
|
||||
"ExplicitScan": {"sequence": []}
|
||||
}
|
||||
if "default" in procdesc:
|
||||
|
@ -8,7 +8,7 @@ from artiq.language import units
|
||||
from artiq.language.core import rpc
|
||||
|
||||
|
||||
__all__ = ["NoDefault",
|
||||
__all__ = ["NoDefault", "DefaultMissing",
|
||||
"PYONValue", "BooleanValue", "EnumerationValue",
|
||||
"NumberValue", "StringValue",
|
||||
"HasEnvironment", "Experiment", "EnvExperiment"]
|
||||
@ -491,7 +491,7 @@ def is_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.
|
||||
"""
|
||||
return is_experiment(o) and not o.__name__.startswith("_")
|
||||
|
@ -307,9 +307,9 @@ def main():
|
||||
elif action == "analyze":
|
||||
try:
|
||||
exp_inst.analyze()
|
||||
put_completed()
|
||||
finally:
|
||||
write_results()
|
||||
put_completed()
|
||||
elif action == "examine":
|
||||
examine(ExamineDeviceMgr, ExamineDatasetMgr, obj["file"])
|
||||
put_completed()
|
||||
|
@ -152,7 +152,12 @@ class SSHClient(Client):
|
||||
if get_pty:
|
||||
chan.get_pty()
|
||||
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)
|
||||
return chan
|
||||
|
||||
|
@ -36,6 +36,12 @@ class RoundtripTest(ExperimentCase):
|
||||
self.assertRoundtrip(True)
|
||||
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):
|
||||
self.assertRoundtrip(numpy.int32(42))
|
||||
self.assertRoundtrip(numpy.int64(42))
|
||||
@ -55,6 +61,12 @@ class RoundtripTest(ExperimentCase):
|
||||
def test_list(self):
|
||||
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):
|
||||
obj = object()
|
||||
self.assertRoundtrip(obj)
|
||||
@ -69,6 +81,7 @@ class RoundtripTest(ExperimentCase):
|
||||
self.assertRoundtrip([(0x12345678, [("foo", [0.0, 1.0], [0, 1])])])
|
||||
|
||||
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.0, 2.0, 3.0]))
|
||||
self.assertArrayRoundtrip(numpy.array(["a", "b", "c"]))
|
||||
@ -206,6 +219,7 @@ class RPCTypesTest(ExperimentCase):
|
||||
class _RPCCalls(EnvExperiment):
|
||||
def build(self):
|
||||
self.setattr_device("core")
|
||||
self._list_int64 = [numpy.int64(1)]
|
||||
|
||||
def args(self, *args) -> TInt32:
|
||||
return len(args)
|
||||
@ -241,6 +255,10 @@ class _RPCCalls(EnvExperiment):
|
||||
def args1kwargs2(self):
|
||||
return self.kwargs("X", a="A", b=1)
|
||||
|
||||
@kernel
|
||||
def list_int64(self):
|
||||
return self._list_int64
|
||||
|
||||
@kernel
|
||||
def numpy_things(self):
|
||||
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.numpy_things(),
|
||||
(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_matrix() == numpy.full((3, 2), 13)).all())
|
||||
self.assertTrue(numpy.isnan(exp.numpy_nan()).all())
|
||||
@ -488,3 +510,23 @@ class AssertTest(ExperimentCase):
|
||||
check_fail(lambda: exp.check(False), "AssertionError")
|
||||
exp.check_msg(True)
|
||||
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.
|
||||
# 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)]
|
||||
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 arg in args:
|
||||
self._test_binop("a" + op + "b", *arg)
|
||||
|
||||
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)
|
||||
matrix = numpy.array([[4, 5, 6], [7, 8, 9]], dtype=typ)
|
||||
for op in ELEM_WISE_BINOPS:
|
||||
@ -88,7 +88,7 @@ class CompareHostDeviceTest(ExperimentCase):
|
||||
self._test_binop(code, matrix, matrix)
|
||||
|
||||
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_b = numpy.array([[7, 8], [9, 10], [11, 12]], dtype=typ)
|
||||
self._test_binop("a @ b", mat_a, mat_b)
|
||||
@ -139,7 +139,7 @@ class _MatrixMult(EnvExperiment):
|
||||
def build(self):
|
||||
self.setattr_device("core")
|
||||
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
|
||||
def run(self):
|
||||
|
51
artiq/test/lit/embedding/class_same_name.py
Normal file
51
artiq/test/lit/embedding/class_same_name.py
Normal file
@ -0,0 +1,51 @@
|
||||
# RUN: %python -m artiq.compiler.testbench.embedding %s
|
||||
|
||||
from artiq.language.core import *
|
||||
|
||||
|
||||
class InnerA:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
@kernel
|
||||
def run_once(self):
|
||||
return self.val
|
||||
|
||||
|
||||
class InnerB:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
@kernel
|
||||
def run_once(self):
|
||||
return self.val
|
||||
|
||||
|
||||
def make_runner(InnerCls, val):
|
||||
class Runner:
|
||||
def __init__(self):
|
||||
self.inner = InnerCls(val)
|
||||
|
||||
@kernel
|
||||
def run_once(self):
|
||||
return self.inner.run_once()
|
||||
|
||||
return Runner()
|
||||
|
||||
|
||||
class Parent:
|
||||
def __init__(self):
|
||||
self.a = make_runner(InnerA, 1)
|
||||
self.b = make_runner(InnerB, 42.0)
|
||||
|
||||
@kernel
|
||||
def run_once(self):
|
||||
return self.a.run_once() + self.b.run_once()
|
||||
|
||||
|
||||
parent = Parent()
|
||||
|
||||
|
||||
@kernel
|
||||
def entrypoint():
|
||||
parent.run_once()
|
16
artiq/test/lit/embedding/mixed_tuple.py
Normal file
16
artiq/test/lit/embedding/mixed_tuple.py
Normal file
@ -0,0 +1,16 @@
|
||||
# RUN: %python -m artiq.compiler.testbench.embedding %s
|
||||
|
||||
from artiq.language.core import *
|
||||
from artiq.language.types import *
|
||||
|
||||
@kernel
|
||||
def consume_tuple(x: TTuple([TInt32, TBool])):
|
||||
print(x)
|
||||
|
||||
@kernel
|
||||
def return_tuple() -> TTuple([TInt32, TBool]):
|
||||
return (123, False)
|
||||
|
||||
@kernel
|
||||
def entrypoint():
|
||||
consume_tuple(return_tuple())
|
@ -1,10 +1,10 @@
|
||||
# RUN: %python -m artiq.compiler.testbench.inferencer %s >%t
|
||||
# RUN: OutputCheck %s --file-to-check=%t
|
||||
|
||||
# Nothing known, as there could be several more dimensions
|
||||
# hidden from view by the array being empty.
|
||||
# CHECK-L: ([]:list(elt='a)):'b
|
||||
# CHECK-L: numpy.array(elt='a, num_dims=1)
|
||||
array([])
|
||||
# CHECK-L: numpy.array(elt='b, num_dims=2)
|
||||
array([[], []])
|
||||
|
||||
# CHECK-L: numpy.array(elt=numpy.int?, num_dims=1)
|
||||
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
|
||||
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
|
||||
b.shape = (5, )
|
||||
|
6
artiq/test/lit/inferencer/if_expr.py
Normal file
6
artiq/test/lit/inferencer/if_expr.py
Normal file
@ -0,0 +1,6 @@
|
||||
# RUN: %python -m artiq.compiler.testbench.inferencer %s >%t
|
||||
# RUN: OutputCheck %s --file-to-check=%t
|
||||
|
||||
# CHECK-L: def foo(val:bool)->numpy.int?:
|
||||
def foo(val):
|
||||
return 1 if val else 0
|
@ -33,8 +33,8 @@ j = []
|
||||
j += [1.0]
|
||||
# CHECK-L: j:list(elt=float)
|
||||
|
||||
1 if a else 2
|
||||
# CHECK-L: 1:numpy.int? if a:numpy.int? else 2:numpy.int?:numpy.int?
|
||||
1 if c else 2
|
||||
# CHECK-L: 1:numpy.int? if c:bool else 2:numpy.int?:numpy.int?
|
||||
|
||||
True and False
|
||||
# CHECK-L: True:bool and False:bool:bool
|
||||
|
24
artiq/test/lit/integration/array_slice.py
Normal file
24
artiq/test/lit/integration/array_slice.py
Normal file
@ -0,0 +1,24 @@
|
||||
# RUN: %python -m artiq.compiler.testbench.jit %s
|
||||
|
||||
a = array([0, 1, 2, 3])
|
||||
|
||||
b = a[2:3]
|
||||
assert b.shape == (1,)
|
||||
assert b[0] == 2
|
||||
b[0] = 5
|
||||
assert a[2] == 5
|
||||
|
||||
b = a[3:2]
|
||||
assert b.shape == (0,)
|
||||
|
||||
c = array([[0, 1], [2, 3]])
|
||||
|
||||
d = c[:1]
|
||||
assert d.shape == (1, 2)
|
||||
assert d[0, 0] == 0
|
||||
assert d[0, 1] == 1
|
||||
d[0, 0] = 5
|
||||
assert c[0, 0] == 5
|
||||
|
||||
d = c[1:0]
|
||||
assert d.shape == (0, 2)
|
44
artiq/test/lit/regression/issue_1506.py
Normal file
44
artiq/test/lit/regression/issue_1506.py
Normal file
@ -0,0 +1,44 @@
|
||||
# RUN: %python -m artiq.compiler.testbench.jit %s
|
||||
|
||||
#
|
||||
# Check various sret-ized return types integrate properly with try/finally, which lowers
|
||||
# to `invoke` on the LLVM level (code adapted from GitHub #1506).
|
||||
#
|
||||
|
||||
LIST = [1, 2]
|
||||
|
||||
|
||||
def get_tuple():
|
||||
return (1, 2)
|
||||
|
||||
|
||||
def get_list():
|
||||
return LIST
|
||||
|
||||
|
||||
def get_range():
|
||||
return range(10)
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
a, b = get_tuple()
|
||||
assert a == 1
|
||||
assert b == 2
|
||||
finally:
|
||||
pass
|
||||
|
||||
try:
|
||||
for _ in get_list():
|
||||
pass
|
||||
finally:
|
||||
pass
|
||||
|
||||
try:
|
||||
for _ in get_range():
|
||||
pass
|
||||
finally:
|
||||
pass
|
||||
|
||||
|
||||
main()
|
13
artiq/test/lit/regression/issue_1627.py
Normal file
13
artiq/test/lit/regression/issue_1627.py
Normal file
@ -0,0 +1,13 @@
|
||||
# RUN: %python -m artiq.compiler.testbench.embedding %s
|
||||
|
||||
from artiq.language.core import *
|
||||
from artiq.language.types import *
|
||||
import numpy as np
|
||||
|
||||
n = 2
|
||||
data = np.zeros((n, n))
|
||||
|
||||
|
||||
@kernel
|
||||
def entrypoint():
|
||||
print(data[:n])
|
19
artiq/test/lit/regression/issue_1632.py
Normal file
19
artiq/test/lit/regression/issue_1632.py
Normal file
@ -0,0 +1,19 @@
|
||||
# RUN: %python -m artiq.compiler.testbench.embedding %s
|
||||
|
||||
from artiq.language.core import *
|
||||
from artiq.language.types import *
|
||||
import numpy as np
|
||||
|
||||
class A:
|
||||
def __init__(self):
|
||||
self.n = 2
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
print([1, 2, 3][:self.n])
|
||||
|
||||
a = A()
|
||||
|
||||
@kernel
|
||||
def entrypoint():
|
||||
a.run()
|
@ -45,6 +45,8 @@ The Python types correspond to ARTIQ type annotations as follows:
|
||||
+---------------+-------------------------+
|
||||
| list of T | TList(T) |
|
||||
+---------------+-------------------------+
|
||||
| NumPy array | TArray(T, num_dims) |
|
||||
+---------------+-------------------------+
|
||||
| range | TRange32, TRange64 |
|
||||
+---------------+-------------------------+
|
||||
| numpy.int32 | TInt32 |
|
||||
|
@ -20,11 +20,10 @@ from unittest.mock import Mock
|
||||
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
|
||||
from sphinx.ext import autodoc
|
||||
from sphinx.util import inspect
|
||||
autodoc.repr = inspect.repr = str
|
||||
import builtins
|
||||
builtins.__in_sphinx__ = True
|
||||
|
||||
|
||||
# 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``.
|
||||
* 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::
|
||||
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
|
||||
-------
|
||||
|
||||
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: ::
|
||||
|
||||
|
@ -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: ::
|
||||
|
||||
$ 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 --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: ::
|
||||
|
||||
@ -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: ::
|
||||
|
||||
$ 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 create -n artiq artiq
|
||||
|
||||
.. 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.
|
||||
|
||||
.. 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. ::
|
||||
|
||||
$ 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 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:
|
||||
|
||||
|
@ -81,11 +81,11 @@ You can write several records at once::
|
||||
|
||||
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::
|
||||
|
||||
$ artiq_coremgmt config delete key1 key2
|
||||
$ artiq_coremgmt config remove key1 key2
|
||||
|
||||
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(),
|
||||
install_requires=requirements,
|
||||
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(),
|
||||
namespace_packages=[],
|
||||
include_package_data=True,
|
||||
|
Loading…
Reference in New Issue
Block a user