forked from M-Labs/artiq
Merge branch 'master' into phaser
* master: (26 commits) fastino: documentation and eem pass-through kasli2: forward sma_clkin to si5324 test: relax test_dma_playback_time on Zynq rpc: fixed _write_bool fastino: document/cleanup build_soc: remove assertion that was used for test runs metlino_sayma_ttl: Fix RTIO frequency & demo code (#1516) Revert "test: temporarily disable test_async_throughput" build_soc: rename identifier_str to gateware_identifier_str test: relax loopback gate timing test: temporarily disable test_async_throughput test: relax test_pulse_rate on Zynq test: skip NonexistentI2CBus if I2C is not supported build_soc: override identifier_str only for gateware examples: add Metlino master, Sayma satellite with TTLOuts via FMC sayma_amc: add support for 4x DIO output channels via FMC fmcdio_vhdci_eem: fix pin naming build_soc: add identifier_str override option RPC: optimization by caching test: improved test_performance ...
This commit is contained in:
commit
50b4eb4840
@ -11,6 +11,7 @@ Highlights:
|
||||
* Performance improvements:
|
||||
- #1432: SERDES TTL inputs can now detect edges on pulses that are shorter
|
||||
than the RTIO period
|
||||
- Improved performance for kernel RPC involving list and array.
|
||||
* Coredevice SI to mu conversions now always return valid codes, or raise a `ValueError`.
|
||||
* Zotino now exposes `voltage_to_mu()`
|
||||
* `ad9910`: The maximum amplitude scale factor is now `0x3fff` (was `0x3ffe`
|
||||
@ -24,6 +25,8 @@ Highlights:
|
||||
* Core device: ``panic_reset 1`` now correctly resets the kernel CPU as well if
|
||||
communication CPU panic occurs.
|
||||
* NumberValue accepts a ``type`` parameter specifying the output as ``int`` or ``float``
|
||||
* A parameter `--identifier-str` has been added to many targets to aid
|
||||
with reproducible builds.
|
||||
|
||||
Breaking changes:
|
||||
|
||||
|
@ -44,15 +44,14 @@ class ReprogrammableIdentifier(Module, AutoCSR):
|
||||
p_INIT=sum(1 << j if c & (1 << i) else 0 for j, c in enumerate(contents)))
|
||||
|
||||
|
||||
def add_identifier(soc, *args, **kwargs):
|
||||
def add_identifier(soc, *args, gateware_identifier_str=None, **kwargs):
|
||||
if hasattr(soc, "identifier"):
|
||||
raise ValueError
|
||||
identifier_str = get_identifier_string(soc, *args, **kwargs)
|
||||
soc.submodules.identifier = ReprogrammableIdentifier(identifier_str)
|
||||
soc.submodules.identifier = ReprogrammableIdentifier(gateware_identifier_str or identifier_str)
|
||||
soc.config["IDENTIFIER_STR"] = identifier_str
|
||||
|
||||
|
||||
|
||||
def build_artiq_soc(soc, argdict):
|
||||
firmware_dir = os.path.join(artiq_dir, "firmware")
|
||||
builder = Builder(soc, **argdict)
|
||||
|
@ -43,9 +43,11 @@ class Reply(Enum):
|
||||
class UnsupportedDevice(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class LoadError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class RPCReturnValueError(ValueError):
|
||||
pass
|
||||
|
||||
@ -53,6 +55,105 @@ class RPCReturnValueError(ValueError):
|
||||
RPCKeyword = namedtuple('RPCKeyword', ['name', 'value'])
|
||||
|
||||
|
||||
def _receive_fraction(kernel, embedding_map):
|
||||
numerator = kernel._read_int64()
|
||||
denominator = kernel._read_int64()
|
||||
return Fraction(numerator, denominator)
|
||||
|
||||
|
||||
def _receive_list(kernel, embedding_map):
|
||||
length = kernel._read_int32()
|
||||
tag = chr(kernel._read_int8())
|
||||
if tag == "b":
|
||||
buffer = kernel._read(length)
|
||||
return list(buffer)
|
||||
elif tag == "i":
|
||||
buffer = kernel._read(4 * length)
|
||||
return list(struct.unpack(">%sl" % length, buffer))
|
||||
elif tag == "I":
|
||||
buffer = kernel._read(8 * length)
|
||||
return list(struct.unpack(">%sq" % length, buffer))
|
||||
elif tag == "f":
|
||||
buffer = kernel._read(8 * length)
|
||||
return list(struct.unpack(">%sd" % length, buffer))
|
||||
else:
|
||||
fn = receivers[tag]
|
||||
elems = []
|
||||
for _ in range(length):
|
||||
# discard tag, as our device would still send the tag for each
|
||||
# non-primitive elements.
|
||||
kernel._read_int8()
|
||||
item = fn(kernel, embedding_map)
|
||||
elems.append(item)
|
||||
return elems
|
||||
|
||||
|
||||
def _receive_array(kernel, embedding_map):
|
||||
num_dims = kernel._read_int8()
|
||||
shape = tuple(kernel._read_int32() for _ in range(num_dims))
|
||||
tag = chr(kernel._read_int8())
|
||||
fn = receivers[tag]
|
||||
length = numpy.prod(shape)
|
||||
if tag == "b":
|
||||
buffer = kernel._read(length)
|
||||
elems = numpy.ndarray((length, ), 'B', buffer)
|
||||
elif tag == "i":
|
||||
buffer = kernel._read(4 * length)
|
||||
elems = numpy.ndarray((length, ), '>i4', buffer)
|
||||
elif tag == "I":
|
||||
buffer = kernel._read(8 * length)
|
||||
elems = numpy.ndarray((length, ), '>i8', buffer)
|
||||
elif tag == "f":
|
||||
buffer = kernel._read(8 * length)
|
||||
elems = numpy.ndarray((length, ), '>d', buffer)
|
||||
else:
|
||||
fn = receivers[tag]
|
||||
elems = []
|
||||
for _ in range(numpy.prod(shape)):
|
||||
# discard the tag
|
||||
kernel._read_int8()
|
||||
item = fn(kernel, embedding_map)
|
||||
elems.append(item)
|
||||
elems = numpy.array(elems)
|
||||
return elems.reshape(shape)
|
||||
|
||||
|
||||
def _receive_range(kernel, embedding_map):
|
||||
start = kernel._receive_rpc_value(embedding_map)
|
||||
stop = kernel._receive_rpc_value(embedding_map)
|
||||
step = kernel._receive_rpc_value(embedding_map)
|
||||
return range(start, stop, step)
|
||||
|
||||
|
||||
def _receive_keyword(kernel, embedding_map):
|
||||
name = kernel._read_string()
|
||||
value = kernel._receive_rpc_value(embedding_map)
|
||||
return RPCKeyword(name, value)
|
||||
|
||||
|
||||
receivers = {
|
||||
"\x00": lambda kernel, embedding_map: kernel._rpc_sentinel,
|
||||
"t": lambda kernel, embedding_map:
|
||||
tuple(kernel._receive_rpc_value(embedding_map)
|
||||
for _ in range(kernel._read_int8())),
|
||||
"n": lambda kernel, embedding_map: None,
|
||||
"b": lambda kernel, embedding_map: bool(kernel._read_int8()),
|
||||
"i": lambda kernel, embedding_map: numpy.int32(kernel._read_int32()),
|
||||
"I": lambda kernel, embedding_map: numpy.int64(kernel._read_int64()),
|
||||
"f": lambda kernel, embedding_map: kernel._read_float64(),
|
||||
"s": lambda kernel, embedding_map: kernel._read_string(),
|
||||
"B": lambda kernel, embedding_map: kernel._read_bytes(),
|
||||
"A": lambda kernel, embedding_map: kernel._read_bytes(),
|
||||
"O": lambda kernel, embedding_map:
|
||||
embedding_map.retrieve_object(kernel._read_int32()),
|
||||
"F": _receive_fraction,
|
||||
"l": _receive_list,
|
||||
"a": _receive_array,
|
||||
"r": _receive_range,
|
||||
"k": _receive_keyword
|
||||
}
|
||||
|
||||
|
||||
class CommKernelDummy:
|
||||
def __init__(self):
|
||||
pass
|
||||
@ -77,6 +178,17 @@ class CommKernel:
|
||||
self._read_type = None
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.read_buffer = bytearray()
|
||||
self.write_buffer = bytearray()
|
||||
|
||||
self.unpack_int32 = struct.Struct(">l").unpack
|
||||
self.unpack_int64 = struct.Struct(">q").unpack
|
||||
self.unpack_float64 = struct.Struct(">d").unpack
|
||||
|
||||
self.pack_header = struct.Struct(">lB").pack
|
||||
self.pack_int32 = struct.Struct(">l").pack
|
||||
self.pack_int64 = struct.Struct(">q").pack
|
||||
self.pack_float64 = struct.Struct(">d").pack
|
||||
|
||||
def open(self):
|
||||
if hasattr(self, "socket"):
|
||||
@ -97,13 +209,18 @@ class CommKernel:
|
||||
#
|
||||
|
||||
def _read(self, length):
|
||||
r = bytes()
|
||||
while len(r) < length:
|
||||
rn = self.socket.recv(min(8192, length - len(r)))
|
||||
if not rn:
|
||||
raise ConnectionResetError("Connection closed")
|
||||
r += rn
|
||||
return r
|
||||
# cache the reads to avoid frequent call to recv
|
||||
while len(self.read_buffer) < length:
|
||||
# the number is just the maximum amount
|
||||
# when there is not much data, it would return earlier
|
||||
diff = length - len(self.read_buffer)
|
||||
flag = 0
|
||||
if diff > 8192:
|
||||
flag |= socket.MSG_WAITALL
|
||||
self.read_buffer += self.socket.recv(8192, flag)
|
||||
result = self.read_buffer[:length]
|
||||
self.read_buffer = self.read_buffer[length:]
|
||||
return result
|
||||
|
||||
def _read_header(self):
|
||||
self.open()
|
||||
@ -111,14 +228,14 @@ class CommKernel:
|
||||
# Wait for a synchronization sequence, 5a 5a 5a 5a.
|
||||
sync_count = 0
|
||||
while sync_count < 4:
|
||||
(sync_byte, ) = struct.unpack("B", self._read(1))
|
||||
sync_byte = self._read(1)[0]
|
||||
if sync_byte == 0x5a:
|
||||
sync_count += 1
|
||||
else:
|
||||
sync_count = 0
|
||||
|
||||
# Read message header.
|
||||
(raw_type, ) = struct.unpack("B", self._read(1))
|
||||
raw_type = self._read(1)[0]
|
||||
self._read_type = Reply(raw_type)
|
||||
|
||||
logger.debug("receiving message: type=%r",
|
||||
@ -134,19 +251,18 @@ class CommKernel:
|
||||
self._read_expect(ty)
|
||||
|
||||
def _read_int8(self):
|
||||
(value, ) = struct.unpack("B", self._read(1))
|
||||
return value
|
||||
return self._read(1)[0]
|
||||
|
||||
def _read_int32(self):
|
||||
(value, ) = struct.unpack(">l", self._read(4))
|
||||
(value, ) = self.unpack_int32(self._read(4))
|
||||
return value
|
||||
|
||||
def _read_int64(self):
|
||||
(value, ) = struct.unpack(">q", self._read(8))
|
||||
(value, ) = self.unpack_int64(self._read(8))
|
||||
return value
|
||||
|
||||
def _read_float64(self):
|
||||
(value, ) = struct.unpack(">d", self._read(8))
|
||||
(value, ) = self.unpack_float64(self._read(8))
|
||||
return value
|
||||
|
||||
def _read_bool(self):
|
||||
@ -163,7 +279,15 @@ class CommKernel:
|
||||
#
|
||||
|
||||
def _write(self, data):
|
||||
self.socket.sendall(data)
|
||||
self.write_buffer += data
|
||||
# if the buffer is already pretty large, send it
|
||||
# the block size is arbitrary, tuning it may improve performance
|
||||
if len(self.write_buffer) > 4096:
|
||||
self._flush()
|
||||
|
||||
def _flush(self):
|
||||
self.socket.sendall(self.write_buffer)
|
||||
self.write_buffer.clear()
|
||||
|
||||
def _write_header(self, ty):
|
||||
self.open()
|
||||
@ -171,7 +295,7 @@ class CommKernel:
|
||||
logger.debug("sending message: type=%r", ty)
|
||||
|
||||
# Write synchronization sequence and header.
|
||||
self._write(struct.pack(">lB", 0x5a5a5a5a, ty.value))
|
||||
self._write(self.pack_header(0x5a5a5a5a, ty.value))
|
||||
|
||||
def _write_empty(self, ty):
|
||||
self._write_header(ty)
|
||||
@ -180,19 +304,19 @@ class CommKernel:
|
||||
self._write(chunk)
|
||||
|
||||
def _write_int8(self, value):
|
||||
self._write(struct.pack("B", value))
|
||||
self._write(value)
|
||||
|
||||
def _write_int32(self, value):
|
||||
self._write(struct.pack(">l", value))
|
||||
self._write(self.pack_int32(value))
|
||||
|
||||
def _write_int64(self, value):
|
||||
self._write(struct.pack(">q", value))
|
||||
self._write(self.pack_int64(value))
|
||||
|
||||
def _write_float64(self, value):
|
||||
self._write(struct.pack(">d", value))
|
||||
self._write(self.pack_float64(value))
|
||||
|
||||
def _write_bool(self, value):
|
||||
self._write(struct.pack("B", value))
|
||||
self._write(b'\x01' if value else b'\x00')
|
||||
|
||||
def _write_bytes(self, value):
|
||||
self._write_int32(len(value))
|
||||
@ -207,6 +331,7 @@ class CommKernel:
|
||||
|
||||
def check_system_info(self):
|
||||
self._write_empty(Request.SystemInfo)
|
||||
self._flush()
|
||||
|
||||
self._read_header()
|
||||
self._read_expect(Reply.SystemInfo)
|
||||
@ -231,6 +356,7 @@ class CommKernel:
|
||||
def load(self, kernel_library):
|
||||
self._write_header(Request.LoadKernel)
|
||||
self._write_bytes(kernel_library)
|
||||
self._flush()
|
||||
|
||||
self._read_header()
|
||||
if self._read_type == Reply.LoadFailed:
|
||||
@ -240,6 +366,7 @@ class CommKernel:
|
||||
|
||||
def run(self):
|
||||
self._write_empty(Request.RunKernel)
|
||||
self._flush()
|
||||
logger.debug("running kernel")
|
||||
|
||||
_rpc_sentinel = object()
|
||||
@ -247,50 +374,8 @@ class CommKernel:
|
||||
# See rpc_proto.rs and compiler/ir.py:rpc_tag.
|
||||
def _receive_rpc_value(self, embedding_map):
|
||||
tag = chr(self._read_int8())
|
||||
if tag == "\x00":
|
||||
return self._rpc_sentinel
|
||||
elif tag == "t":
|
||||
length = self._read_int8()
|
||||
return tuple(self._receive_rpc_value(embedding_map) for _ in range(length))
|
||||
elif tag == "n":
|
||||
return None
|
||||
elif tag == "b":
|
||||
return bool(self._read_int8())
|
||||
elif tag == "i":
|
||||
return numpy.int32(self._read_int32())
|
||||
elif tag == "I":
|
||||
return numpy.int64(self._read_int64())
|
||||
elif tag == "f":
|
||||
return self._read_float64()
|
||||
elif tag == "F":
|
||||
numerator = self._read_int64()
|
||||
denominator = self._read_int64()
|
||||
return Fraction(numerator, denominator)
|
||||
elif tag == "s":
|
||||
return self._read_string()
|
||||
elif tag == "B":
|
||||
return self._read_bytes()
|
||||
elif tag == "A":
|
||||
return self._read_bytes()
|
||||
elif tag == "l":
|
||||
length = self._read_int32()
|
||||
return [self._receive_rpc_value(embedding_map) for _ in range(length)]
|
||||
elif tag == "a":
|
||||
num_dims = self._read_int8()
|
||||
shape = tuple(self._read_int32() for _ in range(num_dims))
|
||||
elems = [self._receive_rpc_value(embedding_map) for _ in range(numpy.prod(shape))]
|
||||
return numpy.array(elems).reshape(shape)
|
||||
elif tag == "r":
|
||||
start = self._receive_rpc_value(embedding_map)
|
||||
stop = self._receive_rpc_value(embedding_map)
|
||||
step = self._receive_rpc_value(embedding_map)
|
||||
return range(start, stop, step)
|
||||
elif tag == "k":
|
||||
name = self._read_string()
|
||||
value = self._receive_rpc_value(embedding_map)
|
||||
return RPCKeyword(name, value)
|
||||
elif tag == "O":
|
||||
return embedding_map.retrieve_object(self._read_int32())
|
||||
if tag in receivers:
|
||||
return receivers.get(tag)(self, embedding_map)
|
||||
else:
|
||||
raise IOError("Unknown RPC value tag: {}".format(repr(tag)))
|
||||
|
||||
@ -340,7 +425,7 @@ class CommKernel:
|
||||
elif tag == "b":
|
||||
check(isinstance(value, bool),
|
||||
lambda: "bool")
|
||||
self._write_int8(value)
|
||||
self._write_bool(value)
|
||||
elif tag == "i":
|
||||
check(isinstance(value, (int, numpy.int32)) and
|
||||
(-2**31 < value < 2**31-1),
|
||||
@ -357,8 +442,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-1) and
|
||||
(-2**63 < value.denominator < 2**63-1),
|
||||
lambda: "64-bit Fraction")
|
||||
self._write_int64(value.numerator)
|
||||
self._write_int64(value.denominator)
|
||||
@ -378,21 +463,44 @@ class CommKernel:
|
||||
check(isinstance(value, list),
|
||||
lambda: "list")
|
||||
self._write_int32(len(value))
|
||||
for elt in value:
|
||||
tags_copy = bytearray(tags)
|
||||
self._send_rpc_value(tags_copy, elt, root, function)
|
||||
tag_element = chr(tags[0])
|
||||
if tag_element == "b":
|
||||
self._write(bytes(value))
|
||||
elif tag_element == "i":
|
||||
self._write(struct.pack(">%sl" % len(value), *value))
|
||||
elif tag_element == "I":
|
||||
self._write(struct.pack(">%sq" % len(value), *value))
|
||||
elif tag_element == "f":
|
||||
self._write(struct.pack(">%sd" % len(value), *value))
|
||||
else:
|
||||
for elt in value:
|
||||
tags_copy = bytearray(tags)
|
||||
self._send_rpc_value(tags_copy, elt, root, function)
|
||||
self._skip_rpc_value(tags)
|
||||
elif tag == "a":
|
||||
check(isinstance(value, numpy.ndarray),
|
||||
lambda: "numpy.ndarray")
|
||||
num_dims = tags.pop(0)
|
||||
check(num_dims == len(value.shape),
|
||||
lambda: "{}-dimensional numpy.ndarray".format(num_dims))
|
||||
lambda: "{}-dimensional numpy.ndarray".format(num_dims))
|
||||
for s in value.shape:
|
||||
self._write_int32(s)
|
||||
for elt in value.reshape((-1,), order="C"):
|
||||
tags_copy = bytearray(tags)
|
||||
self._send_rpc_value(tags_copy, elt, root, function)
|
||||
tag_element = chr(tags[0])
|
||||
if tag_element == "b":
|
||||
self._write(value.reshape((-1,), order="C").tobytes())
|
||||
elif tag_element == "i":
|
||||
array = value.reshape((-1,), order="C").astype('>i4')
|
||||
self._write(array.tobytes())
|
||||
elif tag_element == "I":
|
||||
array = value.reshape((-1,), order="C").astype('>i8')
|
||||
self._write(array.tobytes())
|
||||
elif tag_element == "f":
|
||||
array = value.reshape((-1,), order="C").astype('>d')
|
||||
self._write(array.tobytes())
|
||||
else:
|
||||
for elt in value.reshape((-1,), order="C"):
|
||||
tags_copy = bytearray(tags)
|
||||
self._send_rpc_value(tags_copy, elt, root, function)
|
||||
self._skip_rpc_value(tags)
|
||||
elif tag == "r":
|
||||
check(isinstance(value, range),
|
||||
@ -414,15 +522,15 @@ class CommKernel:
|
||||
return msg
|
||||
|
||||
def _serve_rpc(self, embedding_map):
|
||||
is_async = self._read_bool()
|
||||
service_id = self._read_int32()
|
||||
is_async = self._read_bool()
|
||||
service_id = self._read_int32()
|
||||
args, kwargs = self._receive_rpc_args(embedding_map)
|
||||
return_tags = self._read_bytes()
|
||||
return_tags = self._read_bytes()
|
||||
|
||||
if service_id == 0:
|
||||
service = lambda obj, attr, value: setattr(obj, attr, value)
|
||||
def service(obj, attr, value): return setattr(obj, attr, value)
|
||||
else:
|
||||
service = embedding_map.retrieve_object(service_id)
|
||||
service = embedding_map.retrieve_object(service_id)
|
||||
logger.debug("rpc service: [%d]%r%s %r %r -> %s", service_id, service,
|
||||
(" (async)" if is_async else ""), args, kwargs, return_tags)
|
||||
|
||||
@ -432,15 +540,19 @@ class CommKernel:
|
||||
|
||||
try:
|
||||
result = service(*args, **kwargs)
|
||||
logger.debug("rpc service: %d %r %r = %r", service_id, args, kwargs, result)
|
||||
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._send_rpc_value(bytearray(return_tags),
|
||||
result, result, service)
|
||||
self._flush()
|
||||
except RPCReturnValueError as exn:
|
||||
raise
|
||||
except Exception as exn:
|
||||
logger.debug("rpc service: %d %r %r ! %r", service_id, args, kwargs, exn)
|
||||
logger.debug("rpc service: %d %r %r ! %r",
|
||||
service_id, args, kwargs, exn)
|
||||
|
||||
self._write_header(Request.RPCException)
|
||||
|
||||
@ -479,23 +591,24 @@ class CommKernel:
|
||||
assert False
|
||||
self._write_string(filename)
|
||||
self._write_int32(line)
|
||||
self._write_int32(-1) # column not known
|
||||
self._write_int32(-1) # column not known
|
||||
self._write_string(function)
|
||||
self._flush()
|
||||
|
||||
def _serve_exception(self, embedding_map, symbolizer, demangler):
|
||||
name = self._read_string()
|
||||
message = self._read_string()
|
||||
params = [self._read_int64() for _ in range(3)]
|
||||
name = self._read_string()
|
||||
message = self._read_string()
|
||||
params = [self._read_int64() for _ in range(3)]
|
||||
|
||||
filename = self._read_string()
|
||||
line = self._read_int32()
|
||||
column = self._read_int32()
|
||||
function = self._read_string()
|
||||
filename = self._read_string()
|
||||
line = self._read_int32()
|
||||
column = self._read_int32()
|
||||
function = self._read_string()
|
||||
|
||||
backtrace = [self._read_int32() for _ in range(self._read_int32())]
|
||||
|
||||
traceback = list(reversed(symbolizer(backtrace))) + \
|
||||
[(filename, line, column, *demangler([function]), None)]
|
||||
[(filename, line, column, *demangler([function]), None)]
|
||||
core_exn = exceptions.CoreException(name, message, params, traceback)
|
||||
|
||||
if core_exn.id == 0:
|
||||
|
@ -1,26 +1,44 @@
|
||||
"""RTIO driver for the Fastino 32channel, 16 bit, 2.5 MS/s per channel,
|
||||
streaming DAC.
|
||||
|
||||
TODO: Example, describe update/hold
|
||||
"""
|
||||
|
||||
from artiq.language.core import kernel, portable, delay
|
||||
from artiq.coredevice.rtio import rtio_output, rtio_output_wide, rtio_input_data
|
||||
from artiq.coredevice.rtio import (rtio_output, rtio_output_wide,
|
||||
rtio_input_data)
|
||||
from artiq.language.units import us
|
||||
from artiq.language.types import TInt32, TList, TFloat
|
||||
from artiq.language.types import TInt32, TList
|
||||
|
||||
|
||||
class Fastino:
|
||||
"""Fastino 32-channel, 16-bit, 2.5 MS/s per channel streaming DAC
|
||||
|
||||
The RTIO PHY supports staging DAC data before transmitting them by writing
|
||||
to the DAC RTIO addresses, if a channel is not "held" by setting its bit
|
||||
using :meth:`set_hold`, the next frame will contain the update. For the
|
||||
DACs held, the update is triggered explicitly by setting the corresponding
|
||||
bit using :meth:`set_update`. Update is self-clearing. This enables atomic
|
||||
DAC updates synchronized to a frame edge.
|
||||
|
||||
The `log2_width=0` RTIO layout uses one DAC channel per RTIO address and a
|
||||
dense RTIO address space. The RTIO words are narrow. (32 bit) and
|
||||
few-channel updates are efficient. There is the least amount of DAC state
|
||||
tracking in kernels, at the cost of more DMA and RTIO data.
|
||||
The setting here and in the RTIO PHY (gateware) must match.
|
||||
|
||||
Other `log2_width` (up to `log2_width=5`) settings pack multiple
|
||||
(in powers of two) DAC channels into one group and into one RTIO write.
|
||||
The RTIO data width increases accordingly. The `log2_width`
|
||||
LSBs of the RTIO address for a DAC channel write must be zero and the
|
||||
address space is sparse. For `log2_width=5` the RTIO data is 512 bit wide.
|
||||
|
||||
If `log2_width` is zero, the :meth:`set_dac`/:meth:`set_dac_mu` interface
|
||||
must be used. If non-zero, the :meth:`set_group`/:meth:`set_group_mu`
|
||||
interface must be used.
|
||||
|
||||
:param channel: RTIO channel number
|
||||
:param core_device: Core device name (default: "core")
|
||||
:param log2_width: Width of DAC channel group (power of two,
|
||||
see the RTIO PHY for details). If zero, the
|
||||
:meth:`set_dac`/:meth:`set_dac_mu` interface must be used.
|
||||
If non-zero, the :meth:`set_group`/:meth:`set_group_mu`
|
||||
interface must be used. Value must match the corresponding value
|
||||
in the RTIO PHY.
|
||||
:param log2_width: Width of DAC channel group (logarithm base 2).
|
||||
Value must match the corresponding value in the RTIO PHY (gateware).
|
||||
"""
|
||||
kernel_invariants = {"core", "channel", "width"}
|
||||
|
||||
@ -94,7 +112,10 @@ class Fastino:
|
||||
:param voltage: Voltage in SI Volts.
|
||||
:return: DAC data word in machine units, 16 bit integer.
|
||||
"""
|
||||
return int(round((0x8000/10.)*voltage)) + 0x8000
|
||||
data = int(round((0x8000/10.)*voltage)) + 0x8000
|
||||
if data < 0 or data > 0xffff:
|
||||
raise ValueError("DAC voltage out of bounds")
|
||||
return data
|
||||
|
||||
@portable
|
||||
def voltage_group_to_mu(self, voltage, data):
|
||||
|
95
artiq/examples/metlino_sayma_ttl/device_db.py
Normal file
95
artiq/examples/metlino_sayma_ttl/device_db.py
Normal file
@ -0,0 +1,95 @@
|
||||
core_addr = "192.168.1.65"
|
||||
|
||||
device_db = {
|
||||
"core": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.core",
|
||||
"class": "Core",
|
||||
"arguments": {"host": core_addr, "ref_period": 1/(8*150e6)}
|
||||
},
|
||||
"core_log": {
|
||||
"type": "controller",
|
||||
"host": "::1",
|
||||
"port": 1068,
|
||||
"command": "aqctl_corelog -p {port} --bind {bind} " + core_addr
|
||||
},
|
||||
"core_cache": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.cache",
|
||||
"class": "CoreCache"
|
||||
},
|
||||
"core_dma": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.dma",
|
||||
"class": "CoreDMA"
|
||||
}
|
||||
}
|
||||
|
||||
# master peripherals
|
||||
for i in range(4):
|
||||
device_db["led" + str(i)] = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": i},
|
||||
}
|
||||
|
||||
# DEST#1 peripherals
|
||||
amc_base = 0x070000
|
||||
rtm_base = 0x020000
|
||||
|
||||
for i in range(4):
|
||||
device_db["led" + str(4+i)] = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": amc_base + i},
|
||||
}
|
||||
|
||||
#DIO (EEM0) starting at RTIO channel 0x000056
|
||||
for i in range(8):
|
||||
device_db["ttl" + str(i)] = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": amc_base + 0x000056 + i},
|
||||
}
|
||||
|
||||
#DIO (EEM1) starting at RTIO channel 0x00005e
|
||||
for i in range(8):
|
||||
device_db["ttl" + str(8+i)] = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": amc_base + 0x00005e + i},
|
||||
}
|
||||
|
||||
device_db["fmcdio_dirctl_clk"] = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": amc_base + 0x000066}
|
||||
}
|
||||
|
||||
device_db["fmcdio_dirctl_ser"] = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": amc_base + 0x000067}
|
||||
}
|
||||
|
||||
device_db["fmcdio_dirctl_latch"] = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"arguments": {"channel": amc_base + 0x000068}
|
||||
}
|
||||
|
||||
device_db["fmcdio_dirctl"] = {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.shiftreg",
|
||||
"class": "ShiftReg",
|
||||
"arguments": {"clk": "fmcdio_dirctl_clk",
|
||||
"ser": "fmcdio_dirctl_ser",
|
||||
"latch": "fmcdio_dirctl_latch"}
|
||||
}
|
129
artiq/examples/metlino_sayma_ttl/repository/demo.py
Normal file
129
artiq/examples/metlino_sayma_ttl/repository/demo.py
Normal file
@ -0,0 +1,129 @@
|
||||
import sys
|
||||
import os
|
||||
import select
|
||||
|
||||
from artiq.experiment import *
|
||||
from artiq.coredevice.fmcdio_vhdci_eem import *
|
||||
|
||||
|
||||
def chunker(seq, size):
|
||||
res = []
|
||||
for el in seq:
|
||||
res.append(el)
|
||||
if len(res) == size:
|
||||
yield res
|
||||
res = []
|
||||
if res:
|
||||
yield res
|
||||
|
||||
|
||||
def is_enter_pressed() -> TBool:
|
||||
if os.name == "nt":
|
||||
if msvcrt.kbhit() and msvcrt.getch() == b"\r":
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
if select.select([sys.stdin, ], [], [], 0.0)[0]:
|
||||
sys.stdin.read(1)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class Demo(EnvExperiment):
|
||||
def build(self):
|
||||
self.setattr_device("core")
|
||||
self.setattr_device("fmcdio_dirctl")
|
||||
|
||||
self.leds = dict()
|
||||
self.ttl_outs = dict()
|
||||
|
||||
ddb = self.get_device_db()
|
||||
for name, desc in ddb.items():
|
||||
if isinstance(desc, dict) and desc["type"] == "local":
|
||||
module, cls = desc["module"], desc["class"]
|
||||
if (module, cls) == ("artiq.coredevice.ttl", "TTLOut"):
|
||||
dev = self.get_device(name)
|
||||
if "led" in name: # guess
|
||||
self.leds[name] = dev
|
||||
elif "ttl" in name: # to exclude fmcdio_dirctl
|
||||
self.ttl_outs[name] = dev
|
||||
|
||||
self.leds = sorted(self.leds.items(), key=lambda x: x[1].channel)
|
||||
self.ttl_outs = sorted(self.ttl_outs.items(), key=lambda x: x[1].channel)
|
||||
|
||||
self.dirctl_word = (
|
||||
shiftreg_bits(0, dio_bank0_out_pins | dio_bank1_out_pins) |
|
||||
shiftreg_bits(1, dio_bank0_out_pins | dio_bank1_out_pins)
|
||||
)
|
||||
|
||||
@kernel
|
||||
def init(self):
|
||||
self.core.break_realtime()
|
||||
print("*** Waiting for DRTIO ready...")
|
||||
drtio_indices = [7]
|
||||
for i in drtio_indices:
|
||||
while not self.drtio_is_up(i):
|
||||
pass
|
||||
|
||||
self.fmcdio_dirctl.set(self.dirctl_word)
|
||||
|
||||
@kernel
|
||||
def drtio_is_up(self, drtio_index):
|
||||
if not self.core.get_rtio_destination_status(drtio_index):
|
||||
return False
|
||||
print("DRTIO #", drtio_index, "is ready\n")
|
||||
return True
|
||||
|
||||
@kernel
|
||||
def test_led(self, led):
|
||||
while not is_enter_pressed():
|
||||
self.core.break_realtime()
|
||||
# do not fill the FIFOs too much to avoid long response times
|
||||
t = now_mu() - self.core.seconds_to_mu(0.2)
|
||||
while self.core.get_rtio_counter_mu() < t:
|
||||
pass
|
||||
for i in range(3):
|
||||
led.pulse(100*ms)
|
||||
delay(100*ms)
|
||||
|
||||
def test_leds(self):
|
||||
print("*** Testing LEDs.")
|
||||
print("Check for blinking. Press ENTER when done.")
|
||||
|
||||
for led_name, led_dev in self.leds:
|
||||
print("Testing LED: {}".format(led_name))
|
||||
self.test_led(led_dev)
|
||||
|
||||
@kernel
|
||||
def test_ttl_out_chunk(self, ttl_chunk):
|
||||
while not is_enter_pressed():
|
||||
self.core.break_realtime()
|
||||
for _ in range(50000):
|
||||
i = 0
|
||||
for ttl in ttl_chunk:
|
||||
i += 1
|
||||
for _ in range(i):
|
||||
ttl.pulse(1*us)
|
||||
delay(1*us)
|
||||
delay(10*us)
|
||||
|
||||
def test_ttl_outs(self):
|
||||
print("*** Testing TTL outputs.")
|
||||
print("Outputs are tested in groups of 4. Touch each TTL connector")
|
||||
print("with the oscilloscope probe tip, and check that the number of")
|
||||
print("pulses corresponds to its number in the group.")
|
||||
print("Press ENTER when done.")
|
||||
|
||||
for ttl_chunk in chunker(self.ttl_outs, 4):
|
||||
print("Testing TTL outputs: {}.".format(", ".join(name for name, dev in ttl_chunk)))
|
||||
self.test_ttl_out_chunk([dev for name, dev in ttl_chunk])
|
||||
|
||||
def run(self):
|
||||
self.core.reset()
|
||||
|
||||
if self.leds:
|
||||
self.test_leds()
|
||||
if self.ttl_outs:
|
||||
self.test_ttl_outs()
|
@ -11,6 +11,7 @@ extern crate cslice;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
extern crate byteorder;
|
||||
extern crate io;
|
||||
extern crate dyld;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
use core::str;
|
||||
use core::slice;
|
||||
use cslice::{CSlice, CMutSlice};
|
||||
|
||||
use byteorder::{NetworkEndian, ByteOrder};
|
||||
use io::{ProtoRead, Read, Write, ProtoWrite, Error};
|
||||
use self::tag::{Tag, TagIterator, split_tag};
|
||||
|
||||
@ -53,13 +54,34 @@ unsafe fn recv_value<R, E>(reader: &mut R, tag: Tag, data: &mut *mut (),
|
||||
struct List { elements: *mut (), length: u32 };
|
||||
consume_value!(List, |ptr| {
|
||||
(*ptr).length = reader.read_u32()?;
|
||||
let length = (*ptr).length as usize;
|
||||
|
||||
let tag = it.clone().next().expect("truncated tag");
|
||||
(*ptr).elements = alloc(tag.size() * (*ptr).length as usize)?;
|
||||
|
||||
let mut data = (*ptr).elements;
|
||||
for _ in 0..(*ptr).length as usize {
|
||||
recv_value(reader, tag, &mut data, alloc)?
|
||||
match tag {
|
||||
Tag::Bool => {
|
||||
let dest = slice::from_raw_parts_mut(data as *mut u8, length);
|
||||
reader.read_exact(dest)?;
|
||||
},
|
||||
Tag::Int32 => {
|
||||
let dest = slice::from_raw_parts_mut(data as *mut u8, length * 4);
|
||||
reader.read_exact(dest)?;
|
||||
let dest = slice::from_raw_parts_mut(data as *mut i32, length);
|
||||
NetworkEndian::from_slice_i32(dest);
|
||||
},
|
||||
Tag::Int64 | Tag::Float64 => {
|
||||
let dest = slice::from_raw_parts_mut(data as *mut u8, length * 8);
|
||||
reader.read_exact(dest)?;
|
||||
let dest = slice::from_raw_parts_mut(data as *mut i64, length);
|
||||
NetworkEndian::from_slice_i64(dest);
|
||||
},
|
||||
_ => {
|
||||
for _ in 0..length {
|
||||
recv_value(reader, tag, &mut data, alloc)?
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
@ -72,13 +94,34 @@ unsafe fn recv_value<R, E>(reader: &mut R, tag: Tag, data: &mut *mut (),
|
||||
total_len *= len;
|
||||
consume_value!(u32, |ptr| *ptr = len )
|
||||
}
|
||||
let length = total_len as usize;
|
||||
|
||||
let elt_tag = it.clone().next().expect("truncated tag");
|
||||
*buffer = alloc(elt_tag.size() * total_len as usize)?;
|
||||
|
||||
let mut data = *buffer;
|
||||
for _ in 0..total_len {
|
||||
recv_value(reader, elt_tag, &mut data, alloc)?
|
||||
match elt_tag {
|
||||
Tag::Bool => {
|
||||
let dest = slice::from_raw_parts_mut(data as *mut u8, length);
|
||||
reader.read_exact(dest)?;
|
||||
},
|
||||
Tag::Int32 => {
|
||||
let dest = slice::from_raw_parts_mut(data as *mut u8, length * 4);
|
||||
reader.read_exact(dest)?;
|
||||
let dest = slice::from_raw_parts_mut(data as *mut i32, length);
|
||||
NetworkEndian::from_slice_i32(dest);
|
||||
},
|
||||
Tag::Int64 | Tag::Float64 => {
|
||||
let dest = slice::from_raw_parts_mut(data as *mut u8, length * 8);
|
||||
reader.read_exact(dest)?;
|
||||
let dest = slice::from_raw_parts_mut(data as *mut i64, length);
|
||||
NetworkEndian::from_slice_i64(dest);
|
||||
},
|
||||
_ => {
|
||||
for _ in 0..length {
|
||||
recv_value(reader, elt_tag, &mut data, alloc)?
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
@ -155,11 +198,33 @@ unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ())
|
||||
#[repr(C)]
|
||||
struct List { elements: *const (), length: u32 };
|
||||
consume_value!(List, |ptr| {
|
||||
let length = (*ptr).length as usize;
|
||||
writer.write_u32((*ptr).length)?;
|
||||
let tag = it.clone().next().expect("truncated tag");
|
||||
let mut data = (*ptr).elements;
|
||||
for _ in 0..(*ptr).length as usize {
|
||||
send_value(writer, tag, &mut data)?;
|
||||
writer.write_u8(tag.as_u8())?;
|
||||
match tag {
|
||||
Tag::Bool => {
|
||||
let slice = slice::from_raw_parts(data as *const u8, length);
|
||||
writer.write_all(slice)?;
|
||||
},
|
||||
Tag::Int32 => {
|
||||
let slice = slice::from_raw_parts(data as *const u32, length);
|
||||
for v in slice.iter() {
|
||||
writer.write_u32(*v)?;
|
||||
}
|
||||
},
|
||||
Tag::Int64 | Tag::Float64 => {
|
||||
let slice = slice::from_raw_parts(data as *const u64, length);
|
||||
for v in slice.iter() {
|
||||
writer.write_u64(*v)?;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
for _ in 0..length {
|
||||
send_value(writer, tag, &mut data)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
@ -176,9 +241,31 @@ unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ())
|
||||
total_len *= *len;
|
||||
})
|
||||
}
|
||||
let length = total_len as usize;
|
||||
let mut data = *buffer;
|
||||
for _ in 0..total_len as usize {
|
||||
send_value(writer, elt_tag, &mut data)?;
|
||||
writer.write_u8(elt_tag.as_u8())?;
|
||||
match elt_tag {
|
||||
Tag::Bool => {
|
||||
let slice = slice::from_raw_parts(data as *const u8, length);
|
||||
writer.write_all(slice)?;
|
||||
},
|
||||
Tag::Int32 => {
|
||||
let slice = slice::from_raw_parts(data as *const u32, length);
|
||||
for v in slice.iter() {
|
||||
writer.write_u32(*v)?;
|
||||
}
|
||||
},
|
||||
Tag::Int64 | Tag::Float64 => {
|
||||
let slice = slice::from_raw_parts(data as *const u64, length);
|
||||
for v in slice.iter() {
|
||||
writer.write_u64(*v)?;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
for _ in 0..length {
|
||||
send_value(writer, elt_tag, &mut data)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
@ -107,6 +107,9 @@ fn startup() {
|
||||
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");
|
||||
io_expander0.set_oe(0, 1 << 1).unwrap();
|
||||
io_expander0.set(0, 1, false);
|
||||
io_expander0.service().unwrap();
|
||||
}
|
||||
rtio_clocking::init();
|
||||
|
||||
|
@ -475,6 +475,9 @@ pub extern fn main() -> i32 {
|
||||
io_expander1.set(1, 7, true);
|
||||
io_expander1.service().unwrap();
|
||||
}
|
||||
io_expander0.set_oe(0, 1 << 1).unwrap();
|
||||
io_expander0.set(0, 1, false);
|
||||
io_expander0.service().unwrap();
|
||||
}
|
||||
|
||||
#[cfg(has_si5324)]
|
||||
|
@ -619,12 +619,12 @@ class Fastino(_EEM):
|
||||
) for pol in "pn"]
|
||||
|
||||
@classmethod
|
||||
def add_std(cls, target, eem, iostandard="LVDS_25"):
|
||||
def add_std(cls, target, eem, log2_width, iostandard="LVDS_25"):
|
||||
cls.add_extension(target, eem, iostandard=iostandard)
|
||||
|
||||
phy = fastino.Fastino(target.platform.request("fastino{}_ser_p".format(eem)),
|
||||
target.platform.request("fastino{}_ser_n".format(eem)),
|
||||
log2_width=0)
|
||||
log2_width=log2_width)
|
||||
target.submodules += phy
|
||||
target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
||||
|
||||
|
@ -19,8 +19,9 @@ def _get_connectors():
|
||||
for j, pair in enumerate(eem_fmc_connections[i]):
|
||||
for pn in "n", "p":
|
||||
cc = "cc_" if j == 0 else ""
|
||||
lpc_cc = "CC_" if eem_fmc_connections[i][j] in (0, 1, 17, 18) else ""
|
||||
connections["d{}_{}{}".format(j, cc, pn)] = \
|
||||
"LPC:LA{:02d}_{}{}".format(pair, cc.upper(), pn.upper())
|
||||
"LPC:LA{:02d}_{}{}".format(pair, lpc_cc, pn.upper())
|
||||
connectors.append(("eem{}".format(i), connections))
|
||||
return connectors
|
||||
|
||||
|
@ -79,6 +79,21 @@ class _RTIOCRG(Module, AutoCSR):
|
||||
]
|
||||
|
||||
|
||||
class SMAClkinForward(Module):
|
||||
def __init__(self, platform):
|
||||
sma_clkin = platform.request("sma_clkin")
|
||||
sma_clkin_se = Signal()
|
||||
sma_clkin_buffered = Signal()
|
||||
cdr_clk_se = Signal()
|
||||
cdr_clk = platform.request("cdr_clk")
|
||||
self.specials += [
|
||||
Instance("IBUFDS", i_I=sma_clkin.p, i_IB=sma_clkin.n, o_O=sma_clkin_se),
|
||||
Instance("BUFIO", i_I=sma_clkin_se, o_O=sma_clkin_buffered),
|
||||
Instance("ODDR", i_C=sma_clkin_buffered, i_CE=1, i_D1=0, i_D2=1, o_Q=cdr_clk_se),
|
||||
Instance("OBUFDS", i_I=cdr_clk_se, o_O=cdr_clk.p, o_OB=cdr_clk.n)
|
||||
]
|
||||
|
||||
|
||||
def fix_serdes_timing_path(platform):
|
||||
# ignore timing of path from OSERDESE2 through the pad to ISERDESE2
|
||||
platform.add_platform_command(
|
||||
@ -99,7 +114,7 @@ class StandaloneBase(MiniSoC, AMPSoC):
|
||||
}
|
||||
mem_map.update(MiniSoC.mem_map)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
def __init__(self, gateware_identifier_str=None, **kwargs):
|
||||
MiniSoC.__init__(self,
|
||||
cpu_type="or1k",
|
||||
sdram_controller_type="minicon",
|
||||
@ -109,12 +124,13 @@ class StandaloneBase(MiniSoC, AMPSoC):
|
||||
ethmac_ntxslots=4,
|
||||
**kwargs)
|
||||
AMPSoC.__init__(self)
|
||||
add_identifier(self)
|
||||
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
|
||||
|
||||
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")
|
||||
self.submodules += SMAClkinForward(self.platform)
|
||||
|
||||
i2c = self.platform.request("i2c")
|
||||
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
||||
@ -280,7 +296,7 @@ class MasterBase(MiniSoC, AMPSoC):
|
||||
}
|
||||
mem_map.update(MiniSoC.mem_map)
|
||||
|
||||
def __init__(self, rtio_clk_freq=125e6, enable_sata=False, **kwargs):
|
||||
def __init__(self, rtio_clk_freq=125e6, enable_sata=False, gateware_identifier_str=None, **kwargs):
|
||||
MiniSoC.__init__(self,
|
||||
cpu_type="or1k",
|
||||
sdram_controller_type="minicon",
|
||||
@ -290,10 +306,13 @@ class MasterBase(MiniSoC, AMPSoC):
|
||||
ethmac_ntxslots=4,
|
||||
**kwargs)
|
||||
AMPSoC.__init__(self)
|
||||
add_identifier(self)
|
||||
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
|
||||
|
||||
platform = self.platform
|
||||
|
||||
if platform.hw_rev == "v2.0":
|
||||
self.submodules += SMAClkinForward(platform)
|
||||
|
||||
i2c = self.platform.request("i2c")
|
||||
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
||||
self.csr_devices.append("i2c")
|
||||
@ -453,13 +472,13 @@ class SatelliteBase(BaseSoC):
|
||||
}
|
||||
mem_map.update(BaseSoC.mem_map)
|
||||
|
||||
def __init__(self, rtio_clk_freq=125e6, enable_sata=False, *, with_wrpll=False, **kwargs):
|
||||
def __init__(self, rtio_clk_freq=125e6, enable_sata=False, *, with_wrpll=False, gateware_identifier_str=None, **kwargs):
|
||||
BaseSoC.__init__(self,
|
||||
cpu_type="or1k",
|
||||
sdram_controller_type="minicon",
|
||||
l2_size=128*1024,
|
||||
**kwargs)
|
||||
add_identifier(self)
|
||||
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
|
||||
|
||||
platform = self.platform
|
||||
|
||||
@ -674,11 +693,14 @@ def main():
|
||||
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()
|
||||
try:
|
||||
|
@ -109,7 +109,8 @@ def peripheral_mirny(module, peripheral):
|
||||
def peripheral_fastino(module, peripheral):
|
||||
if len(peripheral["ports"]) != 1:
|
||||
raise ValueError("wrong number of ports")
|
||||
eem.Fastino.add_std(module, peripheral["ports"][0])
|
||||
eem.Fastino.add_std(module, peripheral["ports"][0],
|
||||
peripheral.get("log2_width", 0))
|
||||
|
||||
|
||||
def peripheral_phaser(module, peripheral):
|
||||
@ -259,6 +260,8 @@ def main():
|
||||
parser.set_defaults(output_dir="artiq_kasli")
|
||||
parser.add_argument("description", metavar="DESCRIPTION",
|
||||
help="JSON system description file")
|
||||
parser.add_argument("--gateware-identifier-str", default=None,
|
||||
help="Override ROM identifier")
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.description, "r") as f:
|
||||
@ -276,7 +279,7 @@ def main():
|
||||
else:
|
||||
raise ValueError("Invalid base")
|
||||
|
||||
soc = cls(description, **soc_kasli_argdict(args))
|
||||
soc = cls(description, gateware_identifier_str=args.gateware_identifier_str, **soc_kasli_argdict(args))
|
||||
args.variant = description["variant"]
|
||||
build_artiq_soc(soc, builder_argdict(args))
|
||||
|
||||
|
@ -119,7 +119,7 @@ class _StandaloneBase(MiniSoC, AMPSoC):
|
||||
}
|
||||
mem_map.update(MiniSoC.mem_map)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
def __init__(self, gateware_identifier_str=None, **kwargs):
|
||||
MiniSoC.__init__(self,
|
||||
cpu_type="or1k",
|
||||
sdram_controller_type="minicon",
|
||||
@ -129,7 +129,7 @@ class _StandaloneBase(MiniSoC, AMPSoC):
|
||||
ethmac_ntxslots=4,
|
||||
**kwargs)
|
||||
AMPSoC.__init__(self)
|
||||
add_identifier(self)
|
||||
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
|
||||
|
||||
if isinstance(self.platform.toolchain, XilinxVivadoToolchain):
|
||||
self.platform.toolchain.bitstream_commands.extend([
|
||||
@ -416,6 +416,8 @@ def main():
|
||||
help="variant: "
|
||||
"nist_clock/nist_qc2/sma_spi "
|
||||
"(default: %(default)s)")
|
||||
parser.add_argument("--gateware-identifier-str", default=None,
|
||||
help="Override ROM identifier")
|
||||
args = parser.parse_args()
|
||||
|
||||
variant = args.variant.lower()
|
||||
@ -424,7 +426,7 @@ def main():
|
||||
except KeyError:
|
||||
raise SystemExit("Invalid variant (-V/--variant)")
|
||||
|
||||
soc = cls(**soc_kc705_argdict(args))
|
||||
soc = cls(gateware_identifier_str=args.gateware_identifier_str, **soc_kc705_argdict(args))
|
||||
build_artiq_soc(soc, builder_argdict(args))
|
||||
|
||||
|
||||
|
@ -38,7 +38,7 @@ class Master(MiniSoC, AMPSoC):
|
||||
}
|
||||
mem_map.update(MiniSoC.mem_map)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
def __init__(self, gateware_identifier_str=None, **kwargs):
|
||||
MiniSoC.__init__(self,
|
||||
cpu_type="or1k",
|
||||
sdram_controller_type="minicon",
|
||||
@ -49,7 +49,7 @@ class Master(MiniSoC, AMPSoC):
|
||||
csr_address_width=15,
|
||||
**kwargs)
|
||||
AMPSoC.__init__(self)
|
||||
add_identifier(self)
|
||||
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
|
||||
|
||||
platform = self.platform
|
||||
rtio_clk_freq = 150e6
|
||||
@ -164,9 +164,11 @@ def main():
|
||||
builder_args(parser)
|
||||
soc_sdram_args(parser)
|
||||
parser.set_defaults(output_dir="artiq_metlino")
|
||||
parser.add_argument("--gateware-identifier-str", default=None,
|
||||
help="Override ROM identifier")
|
||||
args = parser.parse_args()
|
||||
args.variant = "master"
|
||||
soc = Master(**soc_sdram_argdict(args))
|
||||
soc = Master(gateware_identifier_str=args.gateware_identifier_str, **soc_sdram_argdict(args))
|
||||
build_artiq_soc(soc, builder_argdict(args))
|
||||
|
||||
|
||||
|
@ -12,8 +12,10 @@ from misoc.interconnect.csr import *
|
||||
from misoc.targets.sayma_amc import *
|
||||
|
||||
from artiq.gateware.amp import AMPSoC
|
||||
from artiq.gateware import eem
|
||||
from artiq.gateware import rtio
|
||||
from artiq.gateware import jesd204_tools
|
||||
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
|
||||
@ -50,7 +52,7 @@ class SatelliteBase(MiniSoC):
|
||||
}
|
||||
mem_map.update(MiniSoC.mem_map)
|
||||
|
||||
def __init__(self, rtio_clk_freq=125e6, identifier_suffix="", with_sfp=False, *, with_wrpll, **kwargs):
|
||||
def __init__(self, rtio_clk_freq=125e6, identifier_suffix="", gateware_identifier_str=None, with_sfp=False, *, with_wrpll, **kwargs):
|
||||
MiniSoC.__init__(self,
|
||||
cpu_type="or1k",
|
||||
sdram_controller_type="minicon",
|
||||
@ -59,7 +61,7 @@ class SatelliteBase(MiniSoC):
|
||||
ethmac_nrxslots=4,
|
||||
ethmac_ntxslots=4,
|
||||
**kwargs)
|
||||
add_identifier(self, suffix=identifier_suffix)
|
||||
add_identifier(self, suffix=identifier_suffix, gateware_identifier_str=gateware_identifier_str)
|
||||
self.rtio_clk_freq = rtio_clk_freq
|
||||
|
||||
platform = self.platform
|
||||
@ -284,7 +286,7 @@ class JDCGSyncDDS(Module, AutoCSR):
|
||||
|
||||
class Satellite(SatelliteBase):
|
||||
"""
|
||||
DRTIO satellite with local DAC/SAWG channels.
|
||||
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,
|
||||
@ -307,7 +309,7 @@ class Satellite(SatelliteBase):
|
||||
self.csr_devices.append("slave_fpga_cfg")
|
||||
self.config["SLAVE_FPGA_GATEWARE"] = 0x200000
|
||||
|
||||
rtio_channels = []
|
||||
self.rtio_channels = rtio_channels = []
|
||||
for i in range(4):
|
||||
phy = ttl_simple.Output(platform.request("user_led", i))
|
||||
self.submodules += phy
|
||||
@ -343,6 +345,27 @@ class Satellite(SatelliteBase):
|
||||
self.jdcg_1.sawgs
|
||||
for phy in sawg.phys)
|
||||
|
||||
# FMC-VHDCI-EEM DIOs x 2 (all OUTPUTs)
|
||||
platform.add_connectors(fmcdio_vhdci_eem.connectors)
|
||||
eem.DIO.add_std(self, 0,
|
||||
ttl_simple.Output, ttl_simple.Output, iostandard="LVDS")
|
||||
eem.DIO.add_std(self, 1,
|
||||
ttl_simple.Output, ttl_simple.Output, iostandard="LVDS")
|
||||
# FMC-DIO-32ch-LVDS-a Direction Control Pins (via shift register) as TTLs x 3
|
||||
platform.add_extension(fmcdio_vhdci_eem.io)
|
||||
print("fmcdio_vhdci_eem.[CLK, SER, LATCH] starting at RTIO channel 0x{:06x}"
|
||||
.format(len(rtio_channels)))
|
||||
fmcdio_dirctl = platform.request("fmcdio_dirctl", 0)
|
||||
fmcdio_dirctl_phys = [
|
||||
ttl_simple.Output(fmcdio_dirctl.clk),
|
||||
ttl_simple.Output(fmcdio_dirctl.ser),
|
||||
ttl_simple.Output(fmcdio_dirctl.latch)
|
||||
]
|
||||
for phy in fmcdio_dirctl_phys:
|
||||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
workaround_us_lvds_tristate(platform)
|
||||
|
||||
self.add_rtio(rtio_channels)
|
||||
|
||||
self.submodules.sysref_sampler = jesd204_tools.SysrefSampler(
|
||||
@ -403,14 +426,24 @@ def main():
|
||||
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("--gateware-identifier-str", default=None,
|
||||
help="Override ROM identifier")
|
||||
args = parser.parse_args()
|
||||
|
||||
variant = args.variant.lower()
|
||||
if variant == "satellite":
|
||||
soc = Satellite(with_sfp=args.sfp, jdcg_type=args.jdcg_type, with_wrpll=args.with_wrpll,
|
||||
**soc_sayma_amc_argdict(args))
|
||||
soc = Satellite(
|
||||
with_sfp=args.sfp,
|
||||
jdcg_type=args.jdcg_type,
|
||||
with_wrpll=args.with_wrpll,
|
||||
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, **soc_sayma_amc_argdict(args))
|
||||
soc = SimpleSatellite(
|
||||
with_sfp=args.sfp,
|
||||
with_wrpll=args.with_wrpll,
|
||||
gateware_identifier_str=args.gateware_identifier_str,
|
||||
**soc_sayma_amc_argdict(args))
|
||||
else:
|
||||
raise SystemExit("Invalid variant (-V/--variant)")
|
||||
|
||||
|
@ -75,11 +75,11 @@ class _SatelliteBase(BaseSoC):
|
||||
}
|
||||
mem_map.update(BaseSoC.mem_map)
|
||||
|
||||
def __init__(self, rtio_clk_freq, *, with_wrpll, **kwargs):
|
||||
def __init__(self, rtio_clk_freq, *, with_wrpll, gateware_identifier_str, **kwargs):
|
||||
BaseSoC.__init__(self,
|
||||
cpu_type="or1k",
|
||||
**kwargs)
|
||||
add_identifier(self)
|
||||
add_identifier(self, gateware_identifier_str=gateware_identifier_str)
|
||||
self.rtio_clk_freq = rtio_clk_freq
|
||||
|
||||
platform = self.platform
|
||||
@ -299,11 +299,15 @@ def main():
|
||||
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")
|
||||
parser.add_argument("--gateware-identifier-str", default=None,
|
||||
help="Override ROM identifier")
|
||||
parser.set_defaults(output_dir=os.path.join("artiq_sayma", "rtm"))
|
||||
args = parser.parse_args()
|
||||
|
||||
soc = Satellite(
|
||||
rtio_clk_freq=1e6*args.rtio_clk_freq, with_wrpll=args.with_wrpll,
|
||||
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))
|
||||
try:
|
||||
|
@ -24,6 +24,7 @@ class I2CSwitch(EnvExperiment):
|
||||
class NonexistentI2CBus(EnvExperiment):
|
||||
def build(self):
|
||||
self.setattr_device("core")
|
||||
self.setattr_device("i2c_switch") # HACK: only run this test on boards with I2C
|
||||
self.broken_switch = PCA9548(self._HasEnvironment__device_mgr, 255)
|
||||
|
||||
@kernel
|
||||
|
@ -1,72 +1,273 @@
|
||||
import os
|
||||
import time
|
||||
import unittest
|
||||
import numpy
|
||||
|
||||
from artiq.experiment import *
|
||||
from artiq.test.hardware_testbench import ExperimentCase
|
||||
|
||||
# large: 1MB payload
|
||||
# small: 1KB payload
|
||||
bytes_large = b"\x00" * (1 << 20)
|
||||
bytes_small = b"\x00" * (1 << 10)
|
||||
|
||||
list_large = [123] * (1 << 18)
|
||||
list_small = [123] * (1 << 8)
|
||||
|
||||
array_large = numpy.array(list_large, numpy.int32)
|
||||
array_small = numpy.array(list_small, numpy.int32)
|
||||
|
||||
byte_list_large = [True] * (1 << 20)
|
||||
byte_list_small = [True] * (1 << 10)
|
||||
|
||||
received_bytes = 0
|
||||
time_start = 0
|
||||
time_end = 0
|
||||
|
||||
class _Transfer(EnvExperiment):
|
||||
def build(self):
|
||||
self.setattr_device("core")
|
||||
self.data = b"\x00"*(10**6)
|
||||
self.count = 10
|
||||
self.h2d = [0.0] * self.count
|
||||
self.d2h = [0.0] * self.count
|
||||
|
||||
@rpc
|
||||
def source(self) -> TBytes:
|
||||
return self.data
|
||||
def get_bytes(self, large: TBool) -> TBytes:
|
||||
if large:
|
||||
return bytes_large
|
||||
else:
|
||||
return bytes_small
|
||||
|
||||
@rpc(flags={"async"})
|
||||
@rpc
|
||||
def get_list(self, large: TBool) -> TList(TInt32):
|
||||
if large:
|
||||
return list_large
|
||||
else:
|
||||
return list_small
|
||||
|
||||
@rpc
|
||||
def get_byte_list(self, large: TBool) -> TList(TBool):
|
||||
if large:
|
||||
return byte_list_large
|
||||
else:
|
||||
return byte_list_small
|
||||
|
||||
@rpc
|
||||
def get_array(self, large: TBool) -> TArray(TInt32):
|
||||
if large:
|
||||
return array_large
|
||||
else:
|
||||
return array_small
|
||||
|
||||
@rpc
|
||||
def get_string_list(self) -> TList(TStr):
|
||||
return string_list
|
||||
|
||||
@rpc
|
||||
def sink(self, data):
|
||||
assert data == self.data
|
||||
pass
|
||||
|
||||
@rpc(flags={"async"})
|
||||
def sink_array(self, data):
|
||||
assert data == [0]*(1 << 15)
|
||||
def sink_async(self, data):
|
||||
global received_bytes, time_start, time_end
|
||||
if received_bytes == 0:
|
||||
time_start = time.time()
|
||||
received_bytes += len(data)
|
||||
if received_bytes == (1024 ** 2)*128:
|
||||
time_end = time.time()
|
||||
|
||||
@rpc
|
||||
def get_async_throughput(self) -> TFloat:
|
||||
return 128.0 / (time_end - time_start)
|
||||
|
||||
@kernel
|
||||
def host_to_device(self):
|
||||
t0 = self.core.get_rtio_counter_mu()
|
||||
data = self.source()
|
||||
t1 = self.core.get_rtio_counter_mu()
|
||||
return len(data)/self.core.mu_to_seconds(t1-t0)
|
||||
def test_bytes(self, large):
|
||||
def inner():
|
||||
t0 = self.core.get_rtio_counter_mu()
|
||||
data = self.get_bytes(large)
|
||||
t1 = self.core.get_rtio_counter_mu()
|
||||
self.sink(data)
|
||||
t2 = self.core.get_rtio_counter_mu()
|
||||
self.h2d[i] = self.core.mu_to_seconds(t1 - t0)
|
||||
self.d2h[i] = self.core.mu_to_seconds(t2 - t1)
|
||||
|
||||
for i in range(self.count):
|
||||
inner()
|
||||
return (self.h2d, self.d2h)
|
||||
|
||||
@kernel
|
||||
def device_to_host(self):
|
||||
t0 = self.core.get_rtio_counter_mu()
|
||||
self.sink(self.data)
|
||||
t1 = self.core.get_rtio_counter_mu()
|
||||
return len(self.data)/self.core.mu_to_seconds(t1-t0)
|
||||
def test_byte_list(self, large):
|
||||
def inner():
|
||||
t0 = self.core.get_rtio_counter_mu()
|
||||
data = self.get_byte_list(large)
|
||||
t1 = self.core.get_rtio_counter_mu()
|
||||
self.sink(data)
|
||||
t2 = self.core.get_rtio_counter_mu()
|
||||
self.h2d[i] = self.core.mu_to_seconds(t1 - t0)
|
||||
self.d2h[i] = self.core.mu_to_seconds(t2 - t1)
|
||||
|
||||
for i in range(self.count):
|
||||
inner()
|
||||
return (self.h2d, self.d2h)
|
||||
|
||||
@kernel
|
||||
def device_to_host_array(self):
|
||||
#data = [[0]*8 for _ in range(1 << 12)]
|
||||
data = [0]*(1 << 15)
|
||||
t0 = self.core.get_rtio_counter_mu()
|
||||
self.sink_array(data)
|
||||
t1 = self.core.get_rtio_counter_mu()
|
||||
return ((len(data)*4)/
|
||||
self.core.mu_to_seconds(t1-t0))
|
||||
def test_list(self, large):
|
||||
def inner():
|
||||
t0 = self.core.get_rtio_counter_mu()
|
||||
data = self.get_list(large)
|
||||
t1 = self.core.get_rtio_counter_mu()
|
||||
self.sink(data)
|
||||
t2 = self.core.get_rtio_counter_mu()
|
||||
self.h2d[i] = self.core.mu_to_seconds(t1 - t0)
|
||||
self.d2h[i] = self.core.mu_to_seconds(t2 - t1)
|
||||
|
||||
for i in range(self.count):
|
||||
inner()
|
||||
return (self.h2d, self.d2h)
|
||||
|
||||
@kernel
|
||||
def test_array(self, large):
|
||||
def inner():
|
||||
t0 = self.core.get_rtio_counter_mu()
|
||||
data = self.get_array(large)
|
||||
t1 = self.core.get_rtio_counter_mu()
|
||||
self.sink(data)
|
||||
t2 = self.core.get_rtio_counter_mu()
|
||||
self.h2d[i] = self.core.mu_to_seconds(t1 - t0)
|
||||
self.d2h[i] = self.core.mu_to_seconds(t2 - t1)
|
||||
|
||||
for i in range(self.count):
|
||||
inner()
|
||||
return (self.h2d, self.d2h)
|
||||
|
||||
@kernel
|
||||
def test_async(self):
|
||||
data = self.get_bytes(True)
|
||||
for _ in range(128):
|
||||
self.sink_async(data)
|
||||
return self.get_async_throughput()
|
||||
|
||||
class TransferTest(ExperimentCase):
|
||||
def test_host_to_device(self):
|
||||
exp = self.create(_Transfer)
|
||||
host_to_device_rate = exp.host_to_device()
|
||||
print(host_to_device_rate/(1024*1024), "MiB/s")
|
||||
self.assertGreater(host_to_device_rate, 2.0e6)
|
||||
@classmethod
|
||||
def setUpClass(self):
|
||||
self.results = []
|
||||
|
||||
def test_device_to_host(self):
|
||||
exp = self.create(_Transfer)
|
||||
device_to_host_rate = exp.device_to_host()
|
||||
print(device_to_host_rate/(1024*1024), "MiB/s")
|
||||
self.assertGreater(device_to_host_rate, 2.2e6)
|
||||
@classmethod
|
||||
def tearDownClass(self):
|
||||
if len(self.results) == 0:
|
||||
return
|
||||
max_length = max(max(len(row[0]) for row in self.results), len("Test"))
|
||||
|
||||
def test_device_to_host_array(self):
|
||||
exp = self.create(_Transfer)
|
||||
rate = exp.device_to_host_array()
|
||||
print(rate/(1024*1024), "MiB/s")
|
||||
self.assertGreater(rate, .15e6)
|
||||
def pad(name):
|
||||
nonlocal max_length
|
||||
return name + " " * (max_length - len(name))
|
||||
print()
|
||||
print("| {} | Mean (MiB/s) | std (MiB/s) |".format(pad("Test")))
|
||||
print("| {} | ------------ | ------------ |".format("-" * max_length))
|
||||
for v in self.results:
|
||||
print("| {} | {:>12.2f} | {:>12.2f} |".format(
|
||||
pad(v[0]), v[1], v[2]))
|
||||
|
||||
def test_bytes_large(self):
|
||||
exp = self.create(_Transfer)
|
||||
results = exp.test_bytes(True)
|
||||
host_to_device = (1 << 20) / numpy.array(results[0], numpy.float64)
|
||||
device_to_host = (1 << 20) / numpy.array(results[1], numpy.float64)
|
||||
host_to_device /= 1024*1024
|
||||
device_to_host /= 1024*1024
|
||||
self.results.append(["Bytes (1MB) H2D", host_to_device.mean(),
|
||||
host_to_device.std()])
|
||||
self.results.append(["Bytes (1MB) D2H", device_to_host.mean(),
|
||||
device_to_host.std()])
|
||||
|
||||
def test_bytes_small(self):
|
||||
exp = self.create(_Transfer)
|
||||
results = exp.test_bytes(False)
|
||||
host_to_device = (1 << 10) / numpy.array(results[0], numpy.float64)
|
||||
device_to_host = (1 << 10) / numpy.array(results[1], numpy.float64)
|
||||
host_to_device /= 1024*1024
|
||||
device_to_host /= 1024*1024
|
||||
self.results.append(["Bytes (1KB) H2D", host_to_device.mean(),
|
||||
host_to_device.std()])
|
||||
self.results.append(["Bytes (1KB) D2H", device_to_host.mean(),
|
||||
device_to_host.std()])
|
||||
|
||||
def test_byte_list_large(self):
|
||||
exp = self.create(_Transfer)
|
||||
results = exp.test_byte_list(True)
|
||||
host_to_device = (1 << 20) / numpy.array(results[0], numpy.float64)
|
||||
device_to_host = (1 << 20) / numpy.array(results[1], numpy.float64)
|
||||
host_to_device /= 1024*1024
|
||||
device_to_host /= 1024*1024
|
||||
self.results.append(["Bytes List (1MB) H2D", host_to_device.mean(),
|
||||
host_to_device.std()])
|
||||
self.results.append(["Bytes List (1MB) D2H", device_to_host.mean(),
|
||||
device_to_host.std()])
|
||||
|
||||
def test_byte_list_small(self):
|
||||
exp = self.create(_Transfer)
|
||||
results = exp.test_byte_list(False)
|
||||
host_to_device = (1 << 10) / numpy.array(results[0], numpy.float64)
|
||||
device_to_host = (1 << 10) / numpy.array(results[1], numpy.float64)
|
||||
host_to_device /= 1024*1024
|
||||
device_to_host /= 1024*1024
|
||||
self.results.append(["Bytes List (1KB) H2D", host_to_device.mean(),
|
||||
host_to_device.std()])
|
||||
self.results.append(["Bytes List (1KB) D2H", device_to_host.mean(),
|
||||
device_to_host.std()])
|
||||
|
||||
def test_list_large(self):
|
||||
exp = self.create(_Transfer)
|
||||
results = exp.test_list(True)
|
||||
host_to_device = (1 << 20) / numpy.array(results[0], numpy.float64)
|
||||
device_to_host = (1 << 20) / numpy.array(results[1], numpy.float64)
|
||||
host_to_device /= 1024*1024
|
||||
device_to_host /= 1024*1024
|
||||
self.results.append(["I32 List (1MB) H2D", host_to_device.mean(),
|
||||
host_to_device.std()])
|
||||
self.results.append(["I32 List (1MB) D2H", device_to_host.mean(),
|
||||
device_to_host.std()])
|
||||
|
||||
def test_list_small(self):
|
||||
exp = self.create(_Transfer)
|
||||
results = exp.test_list(False)
|
||||
host_to_device = (1 << 10) / numpy.array(results[0], numpy.float64)
|
||||
device_to_host = (1 << 10) / numpy.array(results[1], numpy.float64)
|
||||
host_to_device /= 1024*1024
|
||||
device_to_host /= 1024*1024
|
||||
self.results.append(["I32 List (1KB) H2D", host_to_device.mean(),
|
||||
host_to_device.std()])
|
||||
self.results.append(["I32 List (1KB) D2H", device_to_host.mean(),
|
||||
device_to_host.std()])
|
||||
|
||||
def test_array_large(self):
|
||||
exp = self.create(_Transfer)
|
||||
results = exp.test_array(True)
|
||||
host_to_device = (1 << 20) / numpy.array(results[0], numpy.float64)
|
||||
device_to_host = (1 << 20) / numpy.array(results[1], numpy.float64)
|
||||
host_to_device /= 1024*1024
|
||||
device_to_host /= 1024*1024
|
||||
self.results.append(["I32 Array (1MB) H2D", host_to_device.mean(),
|
||||
host_to_device.std()])
|
||||
self.results.append(["I32 Array (1MB) D2H", device_to_host.mean(),
|
||||
device_to_host.std()])
|
||||
|
||||
def test_array_small(self):
|
||||
exp = self.create(_Transfer)
|
||||
results = exp.test_array(False)
|
||||
host_to_device = (1 << 10) / numpy.array(results[0], numpy.float64)
|
||||
device_to_host = (1 << 10) / numpy.array(results[1], numpy.float64)
|
||||
host_to_device /= 1024*1024
|
||||
device_to_host /= 1024*1024
|
||||
self.results.append(["I32 Array (1KB) H2D", host_to_device.mean(),
|
||||
host_to_device.std()])
|
||||
self.results.append(["I32 Array (1KB) D2H", device_to_host.mean(),
|
||||
device_to_host.std()])
|
||||
|
||||
def test_async_throughput(self):
|
||||
exp = self.create(_Transfer)
|
||||
results = exp.test_async()
|
||||
print("Async throughput: {:>6.2f}MiB/s".format(results))
|
||||
|
||||
class _KernelOverhead(EnvExperiment):
|
||||
def build(self):
|
||||
|
@ -12,6 +12,7 @@ from artiq.coredevice import exceptions
|
||||
from artiq.coredevice.comm_mgmt import CommMgmt
|
||||
from artiq.coredevice.comm_analyzer import (StoppedMessage, OutputMessage, InputMessage,
|
||||
decode_dump, get_analyzer_dump)
|
||||
from artiq.compiler.targets import CortexA9Target
|
||||
|
||||
|
||||
artiq_low_latency = os.getenv("ARTIQ_LOW_LATENCY")
|
||||
@ -230,17 +231,18 @@ class LoopbackGateTiming(EnvExperiment):
|
||||
# With the exact delay known, make sure tight gate timings work.
|
||||
# In the most common configuration, 24 mu == 24 ns == 3 coarse periods,
|
||||
# which should be plenty of slack.
|
||||
# FIXME: ZC706 with NIST_QC2 needs 48ns - hw problem?
|
||||
delay_mu(10000)
|
||||
|
||||
gate_start_mu = now_mu()
|
||||
self.loop_in.gate_both_mu(24)
|
||||
self.loop_in.gate_both_mu(48) # XXX
|
||||
gate_end_mu = now_mu()
|
||||
|
||||
# gateware latency offset between gate and input
|
||||
lat_offset = 11*8
|
||||
out_mu = gate_start_mu - loop_delay_mu + lat_offset
|
||||
at_mu(out_mu)
|
||||
self.loop_out.pulse_mu(24)
|
||||
self.loop_out.pulse_mu(48) # XXX
|
||||
|
||||
in_mu = self.loop_in.timestamp_mu(gate_end_mu)
|
||||
print("timings: ", gate_start_mu, in_mu - lat_offset, gate_end_mu)
|
||||
@ -460,11 +462,15 @@ class CoredeviceTest(ExperimentCase):
|
||||
|
||||
def test_pulse_rate(self):
|
||||
"""Minimum interval for sustained TTL output switching"""
|
||||
self.execute(PulseRate)
|
||||
exp = self.execute(PulseRate)
|
||||
rate = self.dataset_mgr.get("pulse_rate")
|
||||
print(rate)
|
||||
self.assertGreater(rate, 100*ns)
|
||||
self.assertLess(rate, 480*ns)
|
||||
if exp.core.target_cls == CortexA9Target:
|
||||
# Crappy AXI PS/PL interface from Xilinx is slow.
|
||||
self.assertLess(rate, 810*ns)
|
||||
else:
|
||||
self.assertLess(rate, 480*ns)
|
||||
|
||||
def test_pulse_rate_ad9914_dds(self):
|
||||
"""Minimum interval for sustained AD9914 DDS frequency switching"""
|
||||
@ -621,11 +627,13 @@ class _DMA(EnvExperiment):
|
||||
self.delta = now_mu() - start
|
||||
|
||||
@kernel
|
||||
def playback_many(self, n):
|
||||
def playback_many(self, n, add_delay=False):
|
||||
handle = self.core_dma.get_handle(self.trace_name)
|
||||
self.core.break_realtime()
|
||||
t1 = self.core.get_rtio_counter_mu()
|
||||
for i in range(n):
|
||||
if add_delay:
|
||||
delay(2*us)
|
||||
self.core_dma.playback_handle(handle)
|
||||
t2 = self.core.get_rtio_counter_mu()
|
||||
self.set_dataset("dma_playback_time", self.core.mu_to_seconds(t2 - t1))
|
||||
@ -718,13 +726,18 @@ class DMATest(ExperimentCase):
|
||||
self.device_mgr.get_desc("ad9914dds0")
|
||||
except KeyError:
|
||||
raise unittest.SkipTest("skipped on Kasli for now")
|
||||
|
||||
exp = self.create(_DMA)
|
||||
is_zynq = exp.core.target_cls == CortexA9Target
|
||||
count = 20000
|
||||
exp.record_many(40)
|
||||
exp.playback_many(count)
|
||||
exp.playback_many(count, is_zynq)
|
||||
dt = self.dataset_mgr.get("dma_playback_time")
|
||||
print("dt={}, dt/count={}".format(dt, dt/count))
|
||||
self.assertLess(dt/count, 4.5*us)
|
||||
if is_zynq:
|
||||
self.assertLess(dt/count, 6.2*us)
|
||||
else:
|
||||
self.assertLess(dt/count, 4.5*us)
|
||||
|
||||
def test_dma_underflow(self):
|
||||
exp = self.create(_DMA)
|
||||
|
Loading…
Reference in New Issue
Block a user