forked from M-Labs/artiq
Merge branch 'phaser' into phaser2
* phaser: (127 commits) phaser: use misoc cordic phaser: fix DDS dummy cfg runtime: disable the Nagle algorithm entirely. runtime: buffer RPC send packets. runtime: don't print debug messages to the UART. runtime: print microsecond timestamps in debug messages. artiq_devtool: abort if build failed. conda: bump llvmlite-artiq dep. conda: bump llvmlite-artiq dep. llvm_ir_generator: use !{→unconditionally.}invariant.load metadata. artiq_devtool: more robust port forwarding. setup: remove paramiko dependency (optional and developer-only) artiq_devtool: implement. artiq_compile: actually disable attribute writeback. conda: use pythonparser 1.0. conda: tighten pythonparser dependency (fixes #600). doc: clarify kernel_invariant doc (fixes #609). compiler: Emit all-kernel_invariant objects as LLVM constants conda: update for LLVM 3.9. add has_dds, use config flags ...
This commit is contained in:
commit
b9ce2bb1f0
@ -1,4 +1,5 @@
|
||||
graft artiq/runtime
|
||||
graft artiq/runtime.rs
|
||||
graft artiq/examples
|
||||
include artiq/gui/logo*.svg
|
||||
include versioneer.py
|
||||
|
@ -13,6 +13,9 @@ Release notes
|
||||
* Dynamic __getattr__'s returning RPC target methods are not supported anymore.
|
||||
Controller driver classes must define all their methods intended for RPC as
|
||||
members.
|
||||
* Datasets requested by experiments are by default archived into their HDF5
|
||||
output. If this behavior is undesirable, turn it off by passing
|
||||
``archive=False`` to ``get_dataset``.
|
||||
|
||||
|
||||
2.0
|
||||
|
@ -187,8 +187,16 @@ class FilesDock(QtWidgets.QDockWidget):
|
||||
except:
|
||||
logger.warning("unable to read metadata from %s",
|
||||
info.filePath(), exc_info=True)
|
||||
rd = dict()
|
||||
if "archive" in f:
|
||||
rd = {k: (True, v.value) for k, v in f["archive"].items()}
|
||||
if "datasets" in f:
|
||||
rd = {k: (True, v.value) for k, v in f["datasets"].items()}
|
||||
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.value)
|
||||
if rd:
|
||||
self.datasets.init(rd)
|
||||
self.dataset_changed.emit(info.filePath())
|
||||
|
||||
|
@ -994,8 +994,24 @@ class Stitcher:
|
||||
else:
|
||||
assert False
|
||||
|
||||
is_async = False
|
||||
if hasattr(host_function, "artiq_embedded") and \
|
||||
"async" in host_function.artiq_embedded.flags:
|
||||
is_async = True
|
||||
|
||||
if not builtins.is_none(ret_type) and is_async:
|
||||
note = diagnostic.Diagnostic("note",
|
||||
"function called here", {},
|
||||
loc)
|
||||
diag = diagnostic.Diagnostic("fatal",
|
||||
"functions that return a value cannot be defined as async RPCs", {},
|
||||
self._function_loc(host_function.artiq_embedded.function),
|
||||
notes=[note])
|
||||
self.engine.process(diag)
|
||||
|
||||
function_type = types.TRPC(ret_type,
|
||||
service=self.embedding_map.store_object(host_function))
|
||||
service=self.embedding_map.store_object(host_function),
|
||||
async=is_async)
|
||||
self.functions[function] = function_type
|
||||
return function_type
|
||||
|
||||
@ -1007,7 +1023,11 @@ class Stitcher:
|
||||
|
||||
if function in self.functions:
|
||||
pass
|
||||
elif not hasattr(host_function, "artiq_embedded"):
|
||||
elif not hasattr(host_function, "artiq_embedded") or \
|
||||
(host_function.artiq_embedded.core_name is None and
|
||||
host_function.artiq_embedded.portable is False and
|
||||
host_function.artiq_embedded.syscall is None and
|
||||
host_function.artiq_embedded.forbidden is False):
|
||||
self._quote_rpc(function, loc)
|
||||
elif host_function.artiq_embedded.function is not None:
|
||||
if host_function.__name__ == "<lambda>":
|
||||
|
@ -40,7 +40,8 @@ class Source:
|
||||
return cls(source.Buffer(f.read(), filename, 1), engine=engine)
|
||||
|
||||
class Module:
|
||||
def __init__(self, src, ref_period=1e-6):
|
||||
def __init__(self, src, ref_period=1e-6, attribute_writeback=True):
|
||||
self.attribute_writeback = attribute_writeback
|
||||
self.engine = src.engine
|
||||
self.embedding_map = src.embedding_map
|
||||
self.name = src.name
|
||||
@ -79,14 +80,8 @@ class Module:
|
||||
llvm_ir_generator = transforms.LLVMIRGenerator(
|
||||
engine=self.engine, module_name=self.name, target=target,
|
||||
embedding_map=self.embedding_map)
|
||||
return llvm_ir_generator.process(self.artiq_ir, attribute_writeback=True)
|
||||
|
||||
def entry_point(self):
|
||||
"""Return the name of the function that is the entry point of this module."""
|
||||
if self.name != "":
|
||||
return self.name + ".__modinit__"
|
||||
else:
|
||||
return "__modinit__"
|
||||
return llvm_ir_generator.process(self.artiq_ir,
|
||||
attribute_writeback=self.attribute_writeback)
|
||||
|
||||
def __repr__(self):
|
||||
printer = types.TypePrinter()
|
||||
|
@ -31,6 +31,7 @@ def globals():
|
||||
# ARTIQ decorators
|
||||
"kernel": builtins.fn_kernel(),
|
||||
"portable": builtins.fn_kernel(),
|
||||
"rpc": builtins.fn_kernel(),
|
||||
|
||||
# ARTIQ context managers
|
||||
"parallel": builtins.obj_parallel(),
|
||||
|
@ -86,14 +86,11 @@ class Target:
|
||||
llmachine = lltarget.create_target_machine(
|
||||
features=",".join(["+{}".format(f) for f in self.features]),
|
||||
reloc="pic", codemodel="default")
|
||||
llmachine.set_verbose(True)
|
||||
llmachine.set_asm_verbosity(True)
|
||||
return llmachine
|
||||
|
||||
def optimize(self, llmodule):
|
||||
llmachine = self.target_machine()
|
||||
llpassmgr = llvm.create_module_pass_manager()
|
||||
llmachine.target_data.add_pass(llpassmgr)
|
||||
llmachine.add_analysis_passes(llpassmgr)
|
||||
|
||||
# Register our alias analysis passes.
|
||||
llpassmgr.add_basic_alias_analysis_pass()
|
||||
@ -161,9 +158,9 @@ class Target:
|
||||
|
||||
return llmachine.emit_object(llmodule)
|
||||
|
||||
def link(self, objects, init_fn):
|
||||
def link(self, objects):
|
||||
"""Link the relocatable objects into a shared library for this target."""
|
||||
with RunTool([self.triple + "-ld", "-shared", "--eh-frame-hdr", "-init", init_fn] +
|
||||
with RunTool([self.triple + "-ld", "-shared", "--eh-frame-hdr"] +
|
||||
["{{obj{}}}".format(index) for index in range(len(objects))] +
|
||||
["-o", "{output}"],
|
||||
output=b"",
|
||||
@ -177,8 +174,7 @@ class Target:
|
||||
return library
|
||||
|
||||
def compile_and_link(self, modules):
|
||||
return self.link([self.assemble(self.compile(module)) for module in modules],
|
||||
init_fn=modules[0].entry_point())
|
||||
return self.link([self.assemble(self.compile(module)) for module in modules])
|
||||
|
||||
def strip(self, library):
|
||||
with RunTool([self.triple + "-strip", "--strip-debug", "{library}", "-o", "{output}"],
|
||||
|
@ -47,7 +47,7 @@ def main():
|
||||
target = OR1KTarget()
|
||||
llvm_ir = target.compile(module)
|
||||
elf_obj = target.assemble(llvm_ir)
|
||||
elf_shlib = target.link([elf_obj], init_fn=module.entry_point())
|
||||
elf_shlib = target.link([elf_obj])
|
||||
|
||||
benchmark(lambda: embed(),
|
||||
"ARTIQ embedding")
|
||||
@ -61,7 +61,7 @@ def main():
|
||||
benchmark(lambda: target.assemble(llvm_ir),
|
||||
"LLVM machine code emission")
|
||||
|
||||
benchmark(lambda: target.link([elf_obj], init_fn=module.entry_point()),
|
||||
benchmark(lambda: target.link([elf_obj]),
|
||||
"Linking")
|
||||
|
||||
benchmark(lambda: target.strip(elf_shlib),
|
||||
|
@ -452,30 +452,30 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
self.current_block = body
|
||||
self.visit(node.body)
|
||||
post_body = self.current_block
|
||||
|
||||
if any(node.orelse):
|
||||
else_tail = self.add_block("while.else")
|
||||
self.current_block = else_tail
|
||||
self.visit(node.orelse)
|
||||
post_else_tail = self.current_block
|
||||
|
||||
tail = self.add_block("while.tail")
|
||||
self.current_block = tail
|
||||
|
||||
if any(node.orelse):
|
||||
if not post_else_tail.is_terminated():
|
||||
post_else_tail.append(ir.Branch(tail))
|
||||
else:
|
||||
else_tail = tail
|
||||
|
||||
post_head.append(ir.BranchIf(cond, body, else_tail))
|
||||
if not post_body.is_terminated():
|
||||
post_body.append(ir.Branch(head))
|
||||
break_block.append(ir.Branch(tail))
|
||||
finally:
|
||||
self.break_target = old_break
|
||||
self.continue_target = old_continue
|
||||
|
||||
if any(node.orelse):
|
||||
else_tail = self.add_block("while.else")
|
||||
self.current_block = else_tail
|
||||
self.visit(node.orelse)
|
||||
post_else_tail = self.current_block
|
||||
|
||||
tail = self.add_block("while.tail")
|
||||
self.current_block = tail
|
||||
|
||||
if any(node.orelse):
|
||||
if not post_else_tail.is_terminated():
|
||||
post_else_tail.append(ir.Branch(tail))
|
||||
else:
|
||||
else_tail = tail
|
||||
|
||||
post_head.append(ir.BranchIf(cond, body, else_tail))
|
||||
if not post_body.is_terminated():
|
||||
post_body.append(ir.Branch(head))
|
||||
break_block.append(ir.Branch(tail))
|
||||
|
||||
def iterable_len(self, value, typ=_size_type):
|
||||
if builtins.is_listish(value.type):
|
||||
if isinstance(value, ir.Constant):
|
||||
@ -541,33 +541,33 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
self.current_assign = None
|
||||
self.visit(node.body)
|
||||
post_body = self.current_block
|
||||
|
||||
if any(node.orelse):
|
||||
else_tail = self.add_block("for.else")
|
||||
self.current_block = else_tail
|
||||
self.visit(node.orelse)
|
||||
post_else_tail = self.current_block
|
||||
|
||||
tail = self.add_block("for.tail")
|
||||
self.current_block = tail
|
||||
|
||||
if any(node.orelse):
|
||||
if not post_else_tail.is_terminated():
|
||||
post_else_tail.append(ir.Branch(tail))
|
||||
else:
|
||||
else_tail = tail
|
||||
|
||||
if node.trip_count is not None:
|
||||
head.append(ir.Loop(node.trip_count, phi, cond, body, else_tail))
|
||||
else:
|
||||
head.append(ir.BranchIf(cond, body, else_tail))
|
||||
if not post_body.is_terminated():
|
||||
post_body.append(ir.Branch(continue_block))
|
||||
break_block.append(ir.Branch(tail))
|
||||
finally:
|
||||
self.break_target = old_break
|
||||
self.continue_target = old_continue
|
||||
|
||||
if any(node.orelse):
|
||||
else_tail = self.add_block("for.else")
|
||||
self.current_block = else_tail
|
||||
self.visit(node.orelse)
|
||||
post_else_tail = self.current_block
|
||||
|
||||
tail = self.add_block("for.tail")
|
||||
self.current_block = tail
|
||||
|
||||
if any(node.orelse):
|
||||
if not post_else_tail.is_terminated():
|
||||
post_else_tail.append(ir.Branch(tail))
|
||||
else:
|
||||
else_tail = tail
|
||||
|
||||
if node.trip_count is not None:
|
||||
head.append(ir.Loop(node.trip_count, phi, cond, body, else_tail))
|
||||
else:
|
||||
head.append(ir.BranchIf(cond, body, else_tail))
|
||||
if not post_body.is_terminated():
|
||||
post_body.append(ir.Branch(continue_block))
|
||||
break_block.append(ir.Branch(tail))
|
||||
|
||||
def visit_Break(self, node):
|
||||
self.append(ir.Branch(self.break_target))
|
||||
|
||||
@ -821,7 +821,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
||||
timeout = self.visit(context_expr_node.args[0])
|
||||
timeout_ms = self.append(ir.Arith(ast.Mult(loc=None), timeout,
|
||||
ir.Constant(1000, builtins.TFloat())))
|
||||
timeout_ms_int = self.append(ir.Coerce(timeout_ms, builtins.TInt32()))
|
||||
timeout_ms_int = self.append(ir.Coerce(timeout_ms, builtins.TInt64()))
|
||||
|
||||
watchdog_id = self.append(ir.Builtin("watchdog_set", [timeout_ms_int],
|
||||
builtins.TInt32()))
|
||||
|
@ -509,7 +509,7 @@ class ASTTypedRewriter(algorithm.Transformer):
|
||||
visit_DictComp = visit_unsupported
|
||||
visit_Ellipsis = visit_unsupported
|
||||
visit_GeneratorExp = visit_unsupported
|
||||
visit_Set = visit_unsupported
|
||||
# visit_Set = visit_unsupported
|
||||
visit_SetComp = visit_unsupported
|
||||
visit_Starred = visit_unsupported
|
||||
visit_Yield = visit_unsupported
|
||||
|
@ -19,6 +19,7 @@ lli32 = ll.IntType(32)
|
||||
lli64 = ll.IntType(64)
|
||||
lldouble = ll.DoubleType()
|
||||
llptr = ll.IntType(8).as_pointer()
|
||||
llptrptr = ll.IntType(8).as_pointer().as_pointer()
|
||||
llmetadata = ll.MetaData()
|
||||
|
||||
|
||||
@ -36,9 +37,16 @@ def memoize(generator):
|
||||
class DebugInfoEmitter:
|
||||
def __init__(self, llmodule):
|
||||
self.llmodule = llmodule
|
||||
self.llsubprograms = []
|
||||
self.llcompileunit = None
|
||||
self.cache = {}
|
||||
|
||||
llident = self.llmodule.add_named_metadata('llvm.ident')
|
||||
llident.add(self.emit_metadata(["ARTIQ"]))
|
||||
|
||||
llflags = self.llmodule.add_named_metadata('llvm.module.flags')
|
||||
llflags.add(self.emit_metadata([2, "Debug Info Version", 3]))
|
||||
llflags.add(self.emit_metadata([2, "Dwarf Version", 4]))
|
||||
|
||||
def emit_metadata(self, operands):
|
||||
def map_operand(operand):
|
||||
if operand is None:
|
||||
@ -66,14 +74,13 @@ class DebugInfoEmitter:
|
||||
})
|
||||
|
||||
@memoize
|
||||
def emit_compile_unit(self, source_buffer, llsubprograms):
|
||||
def emit_compile_unit(self, source_buffer):
|
||||
return self.emit_debug_info("DICompileUnit", {
|
||||
"language": ll.DIToken("DW_LANG_Python"),
|
||||
"file": self.emit_file(source_buffer),
|
||||
"producer": "ARTIQ",
|
||||
"runtimeVersion": 0,
|
||||
"emissionKind": 2, # full=1, lines only=2
|
||||
"subprograms": self.emit_metadata(llsubprograms)
|
||||
}, is_distinct=True)
|
||||
|
||||
@memoize
|
||||
@ -85,21 +92,26 @@ class DebugInfoEmitter:
|
||||
@memoize
|
||||
def emit_subprogram(self, func, llfunc):
|
||||
source_buffer = func.loc.source_buffer
|
||||
|
||||
if self.llcompileunit is None:
|
||||
self.llcompileunit = self.emit_compile_unit(source_buffer)
|
||||
llcompileunits = self.llmodule.add_named_metadata('llvm.dbg.cu')
|
||||
llcompileunits.add(self.llcompileunit)
|
||||
|
||||
display_name = "{}{}".format(func.name, types.TypePrinter().name(func.type))
|
||||
llsubprogram = self.emit_debug_info("DISubprogram", {
|
||||
return self.emit_debug_info("DISubprogram", {
|
||||
"name": func.name,
|
||||
"linkageName": llfunc.name,
|
||||
"type": self.emit_subroutine_type(func.type),
|
||||
"file": self.emit_file(source_buffer),
|
||||
"line": func.loc.line(),
|
||||
"unit": self.llcompileunit,
|
||||
"scope": self.emit_file(source_buffer),
|
||||
"scopeLine": func.loc.line(),
|
||||
"isLocal": func.is_internal,
|
||||
"isDefinition": True,
|
||||
"variables": self.emit_metadata([])
|
||||
}, is_distinct=True)
|
||||
self.llsubprograms.append(llsubprogram)
|
||||
return llsubprogram
|
||||
|
||||
@memoize
|
||||
def emit_loc(self, loc, scope):
|
||||
@ -109,18 +121,6 @@ class DebugInfoEmitter:
|
||||
"scope": scope
|
||||
})
|
||||
|
||||
def finalize(self, source_buffer):
|
||||
llident = self.llmodule.add_named_metadata('llvm.ident')
|
||||
llident.add(self.emit_metadata(["ARTIQ"]))
|
||||
|
||||
llflags = self.llmodule.add_named_metadata('llvm.module.flags')
|
||||
llflags.add(self.emit_metadata([2, "Debug Info Version", 3]))
|
||||
llflags.add(self.emit_metadata([2, "Dwarf Version", 4]))
|
||||
|
||||
llcompile_units = self.llmodule.add_named_metadata('llvm.dbg.cu')
|
||||
llcompile_units.add(self.emit_compile_unit(source_buffer, tuple(self.llsubprograms)))
|
||||
|
||||
|
||||
class LLVMIRGenerator:
|
||||
def __init__(self, engine, module_name, target, embedding_map):
|
||||
self.engine = engine
|
||||
@ -349,14 +349,15 @@ class LLVMIRGenerator:
|
||||
elif name == "strcmp":
|
||||
llty = ll.FunctionType(lli32, [llptr, llptr])
|
||||
elif name == "send_rpc":
|
||||
llty = ll.FunctionType(llvoid, [lli32, llptr],
|
||||
var_arg=True)
|
||||
llty = ll.FunctionType(llvoid, [lli32, llptr, llptrptr])
|
||||
elif name == "send_async_rpc":
|
||||
llty = ll.FunctionType(llvoid, [lli32, llptr, llptrptr])
|
||||
elif name == "recv_rpc":
|
||||
llty = ll.FunctionType(lli32, [llptr])
|
||||
elif name == "now":
|
||||
llty = lli64
|
||||
elif name == "watchdog_set":
|
||||
llty = ll.FunctionType(lli32, [lli32])
|
||||
llty = ll.FunctionType(lli32, [lli64])
|
||||
elif name == "watchdog_clear":
|
||||
llty = ll.FunctionType(llvoid, [lli32])
|
||||
else:
|
||||
@ -366,7 +367,8 @@ class LLVMIRGenerator:
|
||||
llglobal = ll.Function(self.llmodule, llty, name)
|
||||
if name in ("__artiq_raise", "__artiq_reraise", "llvm.trap"):
|
||||
llglobal.attributes.add("noreturn")
|
||||
if name in ("rtio_log", "send_rpc", "watchdog_set", "watchdog_clear",
|
||||
if name in ("rtio_log", "send_rpc", "send_async_rpc",
|
||||
"watchdog_set", "watchdog_clear",
|
||||
self.target.print_function):
|
||||
llglobal.attributes.add("nounwind")
|
||||
else:
|
||||
@ -407,9 +409,6 @@ class LLVMIRGenerator:
|
||||
for func in functions:
|
||||
self.process_function(func)
|
||||
|
||||
if any(functions):
|
||||
self.debug_info_emitter.finalize(functions[0].loc.source_buffer)
|
||||
|
||||
if attribute_writeback and self.embedding_map is not None:
|
||||
self.emit_attribute_writeback()
|
||||
|
||||
@ -652,7 +651,7 @@ class LLVMIRGenerator:
|
||||
llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)],
|
||||
inbounds=True)
|
||||
llouterenv = self.llbuilder.load(llptr)
|
||||
llouterenv.set_metadata('invariant.load', self.empty_metadata)
|
||||
llouterenv.set_metadata('unconditionally.invariant.load', self.empty_metadata)
|
||||
llouterenv.set_metadata('nonnull', self.empty_metadata)
|
||||
return self.llptr_to_var(llouterenv, env_ty.params["$outer"], var_name)
|
||||
|
||||
@ -796,7 +795,7 @@ class LLVMIRGenerator:
|
||||
inbounds=True, name="ptr.{}".format(insn.name))
|
||||
llvalue = self.llbuilder.load(llptr, name="val.{}".format(insn.name))
|
||||
if types.is_instance(typ) and attr in typ.constant_attributes:
|
||||
llvalue.set_metadata('invariant.load', self.empty_metadata)
|
||||
llvalue.set_metadata('unconditionally.invariant.load', self.empty_metadata)
|
||||
if isinstance(llvalue.type, ll.PointerType):
|
||||
self.mark_dereferenceable(llvalue)
|
||||
return llvalue
|
||||
@ -1051,7 +1050,7 @@ class LLVMIRGenerator:
|
||||
llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)],
|
||||
inbounds=True)
|
||||
llouterenv = self.llbuilder.load(llptr)
|
||||
llouterenv.set_metadata('invariant.load', self.empty_metadata)
|
||||
llouterenv.set_metadata('unconditionally.invariant.load', self.empty_metadata)
|
||||
llouterenv.set_metadata('nonnull', self.empty_metadata)
|
||||
return self.llptr_to_var(llouterenv, env_ty.params["$outer"], var_name)
|
||||
else:
|
||||
@ -1233,7 +1232,8 @@ class LLVMIRGenerator:
|
||||
llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [],
|
||||
name="rpc.stack")
|
||||
|
||||
llargs = []
|
||||
llargs = self.llbuilder.alloca(llptr, ll.Constant(lli32, len(args)),
|
||||
name="rpc.args")
|
||||
for index, arg in enumerate(args):
|
||||
if builtins.is_none(arg.type):
|
||||
llargslot = self.llbuilder.alloca(ll.LiteralStructType([]),
|
||||
@ -1243,19 +1243,31 @@ class LLVMIRGenerator:
|
||||
llargslot = self.llbuilder.alloca(llarg.type,
|
||||
name="rpc.arg{}".format(index))
|
||||
self.llbuilder.store(llarg, llargslot)
|
||||
llargs.append(llargslot)
|
||||
llargslot = self.llbuilder.bitcast(llargslot, llptr)
|
||||
|
||||
self.llbuilder.call(self.llbuiltin("send_rpc"),
|
||||
[llservice, lltag] + llargs)
|
||||
llargptr = self.llbuilder.gep(llargs, [ll.Constant(lli32, index)])
|
||||
self.llbuilder.store(llargslot, llargptr)
|
||||
|
||||
if fun_type.async:
|
||||
self.llbuilder.call(self.llbuiltin("send_async_rpc"),
|
||||
[llservice, lltag, llargs])
|
||||
else:
|
||||
self.llbuilder.call(self.llbuiltin("send_rpc"),
|
||||
[llservice, lltag, llargs])
|
||||
|
||||
# Don't waste stack space on saved arguments.
|
||||
self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr])
|
||||
|
||||
if fun_type.async:
|
||||
return ll.Undefined
|
||||
|
||||
# T result = {
|
||||
# void *ptr = NULL;
|
||||
# loop: int size = rpc_recv("tag", ptr);
|
||||
# void *ret_ptr = alloca(sizeof(T));
|
||||
# void *ptr = ret_ptr;
|
||||
# loop: int size = recv_rpc(ptr);
|
||||
# // Non-zero: Provide `size` bytes of extra storage for variable-length data.
|
||||
# if(size) { ptr = alloca(size); goto loop; }
|
||||
# else *(T*)ptr
|
||||
# else *(T*)ret_ptr
|
||||
# }
|
||||
llprehead = self.llbuilder.basic_block
|
||||
llhead = self.llbuilder.append_basic_block(name="rpc.head")
|
||||
@ -1270,7 +1282,7 @@ class LLVMIRGenerator:
|
||||
self.llbuilder.branch(llhead)
|
||||
|
||||
self.llbuilder.position_at_end(llhead)
|
||||
llphi = self.llbuilder.phi(llslotgen.type, name="rpc.size")
|
||||
llphi = self.llbuilder.phi(llslotgen.type, name="rpc.ptr")
|
||||
llphi.add_incoming(llslotgen, llprehead)
|
||||
if llunwindblock:
|
||||
llsize = self.llbuilder.invoke(self.llbuiltin("recv_rpc"), [llphi],
|
||||
@ -1286,6 +1298,7 @@ class LLVMIRGenerator:
|
||||
|
||||
self.llbuilder.position_at_end(llalloc)
|
||||
llalloca = self.llbuilder.alloca(lli8, llsize, name="rpc.alloc")
|
||||
llalloca.align = 4 # maximum alignment required by OR1K ABI
|
||||
llphi.add_incoming(llalloca, llalloc)
|
||||
self.llbuilder.branch(llhead)
|
||||
|
||||
@ -1371,6 +1384,7 @@ class LLVMIRGenerator:
|
||||
def _quote_attributes():
|
||||
llglobal = None
|
||||
llfields = []
|
||||
emit_as_constant = True
|
||||
for attr in typ.attributes:
|
||||
if attr == "__objectid__":
|
||||
objectid = self.embedding_map.store_object(value)
|
||||
@ -1391,6 +1405,8 @@ class LLVMIRGenerator:
|
||||
not types.is_c_function(typ.attributes[attr]))
|
||||
if is_class_function:
|
||||
attrvalue = self.embedding_map.specialize_function(typ.instance, attrvalue)
|
||||
if not (types.is_instance(typ) and attr in typ.constant_attributes):
|
||||
emit_as_constant = False
|
||||
llattrvalue = self._quote(attrvalue, typ.attributes[attr],
|
||||
lambda: path() + [attr])
|
||||
llfields.append(llattrvalue)
|
||||
@ -1398,6 +1414,7 @@ class LLVMIRGenerator:
|
||||
llclosureptr = self.get_global_closure_ptr(typ, attr)
|
||||
llclosureptr.initializer = llattrvalue
|
||||
|
||||
llglobal.global_constant = emit_as_constant
|
||||
llglobal.initializer = ll.Constant(llty.pointee, llfields)
|
||||
llglobal.linkage = "private"
|
||||
return llglobal
|
||||
|
@ -308,20 +308,22 @@ class TRPC(Type):
|
||||
:ivar ret: (:class:`Type`)
|
||||
return type
|
||||
:ivar service: (int) RPC service number
|
||||
:ivar async: (bool) whether the RPC blocks until return
|
||||
"""
|
||||
|
||||
attributes = OrderedDict()
|
||||
|
||||
def __init__(self, ret, service):
|
||||
def __init__(self, ret, service, async=False):
|
||||
assert isinstance(ret, Type)
|
||||
self.ret, self.service = ret, service
|
||||
self.ret, self.service, self.async = ret, service, async
|
||||
|
||||
def find(self):
|
||||
return self
|
||||
|
||||
def unify(self, other):
|
||||
if isinstance(other, TRPC) and \
|
||||
self.service == other.service:
|
||||
self.service == other.service and \
|
||||
self.async == other.async:
|
||||
self.ret.unify(other.ret)
|
||||
elif isinstance(other, TVar):
|
||||
other.unify(self)
|
||||
@ -337,7 +339,8 @@ class TRPC(Type):
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, TRPC) and \
|
||||
self.service == other.service
|
||||
self.service == other.service and \
|
||||
self.async == other.async
|
||||
|
||||
def __ne__(self, other):
|
||||
return not (self == other)
|
||||
@ -727,7 +730,9 @@ class TypePrinter(object):
|
||||
elif isinstance(typ, TFunction):
|
||||
return signature
|
||||
elif isinstance(typ, TRPC):
|
||||
return "[rpc #{}](...)->{}".format(typ.service, self.name(typ.ret, depth + 1))
|
||||
return "[rpc{} #{}](...)->{}".format(typ.service,
|
||||
" async" if typ.async else "",
|
||||
self.name(typ.ret, depth + 1))
|
||||
elif isinstance(typ, TBuiltinFunction):
|
||||
return "<function {}>".format(typ.name)
|
||||
elif isinstance(typ, (TConstructor, TExceptionConstructor)):
|
||||
|
@ -20,7 +20,7 @@ class _H2DMsgType(Enum):
|
||||
IDENT_REQUEST = 3
|
||||
SWITCH_CLOCK = 4
|
||||
|
||||
LOAD_LIBRARY = 5
|
||||
LOAD_KERNEL = 5
|
||||
RUN_KERNEL = 6
|
||||
|
||||
RPC_REPLY = 7
|
||||
@ -68,9 +68,7 @@ RPCKeyword = namedtuple('RPCKeyword', ['name', 'value'])
|
||||
|
||||
class CommGeneric:
|
||||
def __init__(self):
|
||||
self._read_type = self._write_type = None
|
||||
self._read_length = 0
|
||||
self._write_buffer = []
|
||||
self._read_type = None
|
||||
|
||||
def open(self):
|
||||
"""Opens the communication channel.
|
||||
@ -99,10 +97,6 @@ class CommGeneric:
|
||||
def _read_header(self):
|
||||
self.open()
|
||||
|
||||
if self._read_length > 0:
|
||||
raise IOError("Read underrun ({} bytes remaining)".
|
||||
format(self._read_length))
|
||||
|
||||
# Wait for a synchronization sequence, 5a 5a 5a 5a.
|
||||
sync_count = 0
|
||||
while sync_count < 4:
|
||||
@ -113,20 +107,11 @@ class CommGeneric:
|
||||
sync_count = 0
|
||||
|
||||
# Read message header.
|
||||
(self._read_length, ) = struct.unpack(">l", self.read(4))
|
||||
if not self._read_length: # inband connection close
|
||||
raise OSError("Connection closed")
|
||||
|
||||
(raw_type, ) = struct.unpack("B", self.read(1))
|
||||
self._read_type = _D2HMsgType(raw_type)
|
||||
|
||||
if self._read_length < 9:
|
||||
raise IOError("Read overrun in message header ({} remaining)".
|
||||
format(self._read_length))
|
||||
self._read_length -= 9
|
||||
|
||||
logger.debug("receiving message: type=%r length=%d",
|
||||
self._read_type, self._read_length)
|
||||
logger.debug("receiving message: type=%r",
|
||||
self._read_type)
|
||||
|
||||
def _read_expect(self, ty):
|
||||
if self._read_type != ty:
|
||||
@ -138,12 +123,6 @@ class CommGeneric:
|
||||
self._read_expect(ty)
|
||||
|
||||
def _read_chunk(self, length):
|
||||
if self._read_length < length:
|
||||
raise IOError("Read overrun while trying to read {} bytes ({} remaining)"
|
||||
" in packet {}".
|
||||
format(length, self._read_length, self._read_type))
|
||||
|
||||
self._read_length -= length
|
||||
return self.read(length)
|
||||
|
||||
def _read_int8(self):
|
||||
@ -162,11 +141,14 @@ class CommGeneric:
|
||||
(value, ) = struct.unpack(">d", self._read_chunk(8))
|
||||
return value
|
||||
|
||||
def _read_bool(self):
|
||||
return True if self._read_int8() else False
|
||||
|
||||
def _read_bytes(self):
|
||||
return self._read_chunk(self._read_int32())
|
||||
|
||||
def _read_string(self):
|
||||
return self._read_bytes()[:-1].decode("utf-8")
|
||||
return self._read_bytes().decode("utf-8")
|
||||
|
||||
#
|
||||
# Writer interface
|
||||
@ -175,46 +157,38 @@ class CommGeneric:
|
||||
def _write_header(self, ty):
|
||||
self.open()
|
||||
|
||||
logger.debug("preparing to send message: type=%r", ty)
|
||||
self._write_type = ty
|
||||
self._write_buffer = []
|
||||
logger.debug("sending message: type=%r", ty)
|
||||
|
||||
def _write_flush(self):
|
||||
# Calculate message size.
|
||||
length = sum([len(chunk) for chunk in self._write_buffer])
|
||||
logger.debug("sending message: type=%r length=%d", self._write_type, length)
|
||||
|
||||
# Write synchronization sequence, header and body.
|
||||
self.write(struct.pack(">llB", 0x5a5a5a5a,
|
||||
9 + length, self._write_type.value))
|
||||
for chunk in self._write_buffer:
|
||||
self.write(chunk)
|
||||
# Write synchronization sequence and header.
|
||||
self.write(struct.pack(">lB", 0x5a5a5a5a, ty.value))
|
||||
|
||||
def _write_empty(self, ty):
|
||||
self._write_header(ty)
|
||||
self._write_flush()
|
||||
|
||||
def _write_chunk(self, chunk):
|
||||
self._write_buffer.append(chunk)
|
||||
self.write(chunk)
|
||||
|
||||
def _write_int8(self, value):
|
||||
self._write_buffer.append(struct.pack("B", value))
|
||||
self.write(struct.pack("B", value))
|
||||
|
||||
def _write_int32(self, value):
|
||||
self._write_buffer.append(struct.pack(">l", value))
|
||||
self.write(struct.pack(">l", value))
|
||||
|
||||
def _write_int64(self, value):
|
||||
self._write_buffer.append(struct.pack(">q", value))
|
||||
self.write(struct.pack(">q", value))
|
||||
|
||||
def _write_float64(self, value):
|
||||
self._write_buffer.append(struct.pack(">d", value))
|
||||
self.write(struct.pack(">d", value))
|
||||
|
||||
def _write_bool(self, value):
|
||||
self.write(struct.pack("B", value))
|
||||
|
||||
def _write_bytes(self, value):
|
||||
self._write_int32(len(value))
|
||||
self._write_buffer.append(value)
|
||||
self.write(value)
|
||||
|
||||
def _write_string(self, value):
|
||||
self._write_bytes(value.encode("utf-8") + b"\0")
|
||||
self._write_bytes(value.encode("utf-8"))
|
||||
|
||||
#
|
||||
# Exported APIs
|
||||
@ -232,7 +206,7 @@ class CommGeneric:
|
||||
if runtime_id != b"AROR":
|
||||
raise UnsupportedDevice("Unsupported runtime ID: {}"
|
||||
.format(runtime_id))
|
||||
gateware_version = self._read_chunk(self._read_length).decode("utf-8")
|
||||
gateware_version = self._read_string()
|
||||
if gateware_version != software_version and \
|
||||
gateware_version + ".dirty" != software_version:
|
||||
logger.warning("Mismatch between gateware (%s) "
|
||||
@ -242,7 +216,6 @@ class CommGeneric:
|
||||
def switch_clock(self, external):
|
||||
self._write_header(_H2DMsgType.SWITCH_CLOCK)
|
||||
self._write_int8(external)
|
||||
self._write_flush()
|
||||
|
||||
self._read_empty(_D2HMsgType.CLOCK_SWITCH_COMPLETED)
|
||||
|
||||
@ -251,7 +224,7 @@ class CommGeneric:
|
||||
|
||||
self._read_header()
|
||||
self._read_expect(_D2HMsgType.LOG_REPLY)
|
||||
return self._read_chunk(self._read_length).decode("utf-8", "replace")
|
||||
return self._read_string()
|
||||
|
||||
def clear_log(self):
|
||||
self._write_empty(_H2DMsgType.LOG_CLEAR)
|
||||
@ -261,17 +234,15 @@ class CommGeneric:
|
||||
def flash_storage_read(self, key):
|
||||
self._write_header(_H2DMsgType.FLASH_READ_REQUEST)
|
||||
self._write_string(key)
|
||||
self._write_flush()
|
||||
|
||||
self._read_header()
|
||||
self._read_expect(_D2HMsgType.FLASH_READ_REPLY)
|
||||
return self._read_chunk(self._read_length)
|
||||
return self._read_string()
|
||||
|
||||
def flash_storage_write(self, key, value):
|
||||
self._write_header(_H2DMsgType.FLASH_WRITE_REQUEST)
|
||||
self._write_string(key)
|
||||
self._write_bytes(value)
|
||||
self._write_flush()
|
||||
|
||||
self._read_header()
|
||||
if self._read_type == _D2HMsgType.FLASH_ERROR_REPLY:
|
||||
@ -287,14 +258,12 @@ class CommGeneric:
|
||||
def flash_storage_remove(self, key):
|
||||
self._write_header(_H2DMsgType.FLASH_REMOVE_REQUEST)
|
||||
self._write_string(key)
|
||||
self._write_flush()
|
||||
|
||||
self._read_empty(_D2HMsgType.FLASH_OK_REPLY)
|
||||
|
||||
def load(self, kernel_library):
|
||||
self._write_header(_H2DMsgType.LOAD_LIBRARY)
|
||||
self._write_chunk(kernel_library)
|
||||
self._write_flush()
|
||||
self._write_header(_H2DMsgType.LOAD_KERNEL)
|
||||
self._write_bytes(kernel_library)
|
||||
|
||||
self._read_empty(_D2HMsgType.LOAD_COMPLETED)
|
||||
|
||||
@ -442,25 +411,29 @@ class CommGeneric:
|
||||
raise IOError("Unknown RPC value tag: {}".format(repr(tag)))
|
||||
|
||||
def _serve_rpc(self, embedding_map):
|
||||
service_id = self._read_int32()
|
||||
if service_id == 0:
|
||||
service = lambda obj, attr, value: setattr(obj, attr, value)
|
||||
else:
|
||||
service = embedding_map.retrieve_object(service_id)
|
||||
|
||||
async = self._read_bool()
|
||||
service_id = self._read_int32()
|
||||
args, kwargs = self._receive_rpc_args(embedding_map)
|
||||
return_tags = self._read_bytes()
|
||||
logger.debug("rpc service: [%d]%r %r %r -> %s", service_id, service, args, kwargs, return_tags)
|
||||
|
||||
if service_id is 0:
|
||||
service = lambda obj, attr, value: setattr(obj, attr, value)
|
||||
else:
|
||||
service = embedding_map.retrieve_object(service_id)
|
||||
logger.debug("rpc service: [%d]%r%s %r %r -> %s", service_id, service,
|
||||
(" (async)" if async else ""), args, kwargs, return_tags)
|
||||
|
||||
if async:
|
||||
service(*args, **kwargs)
|
||||
return
|
||||
|
||||
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)
|
||||
|
||||
if service_id != 0:
|
||||
self._write_header(_H2DMsgType.RPC_REPLY)
|
||||
self._write_bytes(return_tags)
|
||||
self._send_rpc_value(bytearray(return_tags), result, result, service)
|
||||
self._write_flush()
|
||||
self._write_header(_H2DMsgType.RPC_REPLY)
|
||||
self._write_bytes(return_tags)
|
||||
self._send_rpc_value(bytearray(return_tags), result, result, service)
|
||||
except Exception as exn:
|
||||
logger.debug("rpc service: %d %r %r ! %r", service_id, args, kwargs, exn)
|
||||
|
||||
@ -503,8 +476,6 @@ class CommGeneric:
|
||||
self._write_int32(-1) # column not known
|
||||
self._write_string(function)
|
||||
|
||||
self._write_flush()
|
||||
|
||||
def _serve_exception(self, embedding_map, symbolizer, demangler):
|
||||
name = self._read_string()
|
||||
message = self._read_string()
|
||||
|
@ -29,7 +29,6 @@ def initialize_connection(host, port):
|
||||
sock.settimeout(None)
|
||||
set_keepalive(sock, 3, 2, 3)
|
||||
logger.debug("connected to host %s on port %d", host, port)
|
||||
sock.sendall(b"ARTIQ coredev\n")
|
||||
return sock
|
||||
|
||||
|
||||
@ -44,6 +43,7 @@ class Comm(CommGeneric):
|
||||
if hasattr(self, "socket"):
|
||||
return
|
||||
self.socket = initialize_connection(self.host, self.port)
|
||||
self.socket.sendall(b"ARTIQ coredev\n")
|
||||
|
||||
def close(self):
|
||||
if not hasattr(self, "socket"):
|
||||
|
@ -81,7 +81,7 @@ class Core:
|
||||
self.core = self
|
||||
self.comm.core = self
|
||||
|
||||
def compile(self, function, args, kwargs, set_result=None, with_attr_writeback=True):
|
||||
def compile(self, function, args, kwargs, set_result=None, attribute_writeback=True):
|
||||
try:
|
||||
engine = _DiagnosticEngine(all_errors_are_fatal=True)
|
||||
|
||||
@ -89,7 +89,9 @@ class Core:
|
||||
stitcher.stitch_call(function, args, kwargs, set_result)
|
||||
stitcher.finalize()
|
||||
|
||||
module = Module(stitcher, ref_period=self.ref_period)
|
||||
module = Module(stitcher,
|
||||
ref_period=self.ref_period,
|
||||
attribute_writeback=attribute_writeback)
|
||||
target = OR1KTarget()
|
||||
|
||||
library = target.compile_and_link([module])
|
||||
@ -103,6 +105,7 @@ class Core:
|
||||
|
||||
def run(self, function, args, kwargs):
|
||||
result = None
|
||||
@rpc(flags={"async"})
|
||||
def set_result(new_result):
|
||||
nonlocal result
|
||||
result = new_result
|
||||
|
@ -7,6 +7,7 @@ from PyQt5 import QtCore, QtWidgets
|
||||
from artiq.tools import short_format
|
||||
from artiq.gui.tools import LayoutWidget, QRecursiveFilterProxyModel
|
||||
from artiq.gui.models import DictSyncTreeSepModel
|
||||
from artiq.gui.scientific_spinbox import ScientificSpinBox
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -50,10 +51,10 @@ class Editor(QtWidgets.QDialog):
|
||||
|
||||
class NumberEditor(Editor):
|
||||
def get_edit_widget(self, initial_value):
|
||||
self.edit_widget = QtWidgets.QDoubleSpinBox()
|
||||
self.edit_widget.setMinimum(float("-inf"))
|
||||
self.edit_widget.setMaximum(float("+inf"))
|
||||
self.edit_widget.setDecimals(8)
|
||||
self.edit_widget = ScientificSpinBox()
|
||||
self.edit_widget.setDecimals(13)
|
||||
self.edit_widget.setPrecision()
|
||||
self.edit_widget.setRelativeStep()
|
||||
self.edit_widget.setValue(float(initial_value))
|
||||
return self.edit_widget
|
||||
|
||||
|
@ -23,35 +23,26 @@ _mode_enc = {
|
||||
}
|
||||
|
||||
|
||||
class _MoninjWidget(QtWidgets.QFrame):
|
||||
def __init__(self):
|
||||
QtWidgets.QFrame.__init__(self)
|
||||
qfm = QtGui.QFontMetrics(self.font())
|
||||
self._size = QtCore.QSize(
|
||||
18*qfm.averageCharWidth(),
|
||||
6*qfm.lineSpacing())
|
||||
self.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
|
||||
|
||||
self.setFrameShape(QtWidgets.QFrame.Box)
|
||||
self.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
|
||||
def sizeHint(self):
|
||||
return self._size
|
||||
|
||||
|
||||
class _TTLWidget(_MoninjWidget):
|
||||
class _TTLWidget(QtWidgets.QFrame):
|
||||
def __init__(self, channel, send_to_device, force_out, title):
|
||||
QtWidgets.QFrame.__init__(self)
|
||||
|
||||
self.channel = channel
|
||||
self.send_to_device = send_to_device
|
||||
self.force_out = force_out
|
||||
|
||||
_MoninjWidget.__init__(self)
|
||||
self.setFrameShape(QtWidgets.QFrame.Box)
|
||||
self.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
|
||||
grid = QtWidgets.QGridLayout()
|
||||
grid.setContentsMargins(0, 0, 0, 0)
|
||||
grid.setHorizontalSpacing(0)
|
||||
grid.setVerticalSpacing(0)
|
||||
self.setLayout(grid)
|
||||
label = QtWidgets.QLabel(title)
|
||||
label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
label.setWordWrap(True)
|
||||
label.setSizePolicy(QtWidgets.QSizePolicy.Ignored,
|
||||
QtWidgets.QSizePolicy.Preferred)
|
||||
grid.addWidget(label, 1, 1)
|
||||
|
||||
self.stack = QtWidgets.QStackedWidget()
|
||||
@ -62,9 +53,18 @@ class _TTLWidget(_MoninjWidget):
|
||||
self.stack.addWidget(self.direction)
|
||||
|
||||
grid_cb = LayoutWidget()
|
||||
self.override = QtWidgets.QCheckBox("Override")
|
||||
grid_cb.layout.setContentsMargins(0, 0, 0, 0)
|
||||
grid_cb.layout.setHorizontalSpacing(0)
|
||||
grid_cb.layout.setVerticalSpacing(0)
|
||||
self.override = QtWidgets.QToolButton()
|
||||
self.override.setText("OVR")
|
||||
self.override.setCheckable(True)
|
||||
self.override.setToolTip("Override")
|
||||
grid_cb.addWidget(self.override, 3, 1)
|
||||
self.level = QtWidgets.QCheckBox("Level")
|
||||
self.level = QtWidgets.QToolButton()
|
||||
self.level.setText("LVL")
|
||||
self.level.setCheckable(True)
|
||||
self.level.setToolTip("Level")
|
||||
grid_cb.addWidget(self.level, 3, 2)
|
||||
self.stack.addWidget(grid_cb)
|
||||
|
||||
@ -78,19 +78,19 @@ class _TTLWidget(_MoninjWidget):
|
||||
grid.setRowStretch(4, 1)
|
||||
|
||||
self.programmatic_change = False
|
||||
self.override.stateChanged.connect(self.override_toggled)
|
||||
self.level.stateChanged.connect(self.level_toggled)
|
||||
self.override.clicked.connect(self.override_toggled)
|
||||
self.level.clicked.connect(self.level_toggled)
|
||||
|
||||
self.set_value(0, False, False)
|
||||
|
||||
def enterEvent(self, event):
|
||||
self.stack.setCurrentIndex(1)
|
||||
_MoninjWidget.enterEvent(self, event)
|
||||
QtWidgets.QFrame.enterEvent(self, event)
|
||||
|
||||
def leaveEvent(self, event):
|
||||
if not self.override.isChecked():
|
||||
self.stack.setCurrentIndex(0)
|
||||
_MoninjWidget.leaveEvent(self, event)
|
||||
QtWidgets.QFrame.leaveEvent(self, event)
|
||||
|
||||
def override_toggled(self, override):
|
||||
if self.programmatic_change:
|
||||
@ -125,11 +125,11 @@ class _TTLWidget(_MoninjWidget):
|
||||
color = " color=\"red\""
|
||||
else:
|
||||
color = ""
|
||||
self.value.setText("<font size=\"9\"{}>{}</font>".format(
|
||||
self.value.setText("<font size=\"5\"{}>{}</font>".format(
|
||||
color, value_s))
|
||||
oe = oe or self.force_out
|
||||
direction = "OUT" if oe else "IN"
|
||||
self.direction.setText("<font size=\"1\">" + direction + "</font>")
|
||||
self.direction.setText("<font size=\"2\">" + direction + "</font>")
|
||||
|
||||
self.programmatic_change = True
|
||||
try:
|
||||
@ -144,24 +144,30 @@ class _TTLWidget(_MoninjWidget):
|
||||
return self.channel
|
||||
|
||||
|
||||
class _DDSWidget(_MoninjWidget):
|
||||
class _DDSWidget(QtWidgets.QFrame):
|
||||
def __init__(self, bus_channel, channel, sysclk, title):
|
||||
QtWidgets.QFrame.__init__(self)
|
||||
|
||||
self.bus_channel = bus_channel
|
||||
self.channel = channel
|
||||
self.sysclk = sysclk
|
||||
|
||||
_MoninjWidget.__init__(self)
|
||||
self.setFrameShape(QtWidgets.QFrame.Box)
|
||||
self.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
|
||||
grid = QtWidgets.QGridLayout()
|
||||
grid.setContentsMargins(0, 0, 0, 0)
|
||||
grid.setHorizontalSpacing(0)
|
||||
grid.setVerticalSpacing(0)
|
||||
self.setLayout(grid)
|
||||
label = QtWidgets.QLabel(title)
|
||||
label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
label.setWordWrap(True)
|
||||
label.setSizePolicy(QtWidgets.QSizePolicy.Ignored,
|
||||
QtWidgets.QSizePolicy.Preferred)
|
||||
grid.addWidget(label, 1, 1)
|
||||
|
||||
self.value = QtWidgets.QLabel()
|
||||
self.value.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.value.setWordWrap(True)
|
||||
grid.addWidget(self.value, 2, 1, 6, 1)
|
||||
|
||||
grid.setRowStretch(1, 1)
|
||||
@ -172,7 +178,7 @@ class _DDSWidget(_MoninjWidget):
|
||||
|
||||
def set_value(self, ftw):
|
||||
frequency = ftw*self.sysclk()/2**32
|
||||
self.value.setText("<font size=\"5\">{:.7f} MHz</font>"
|
||||
self.value.setText("<font size=\"4\">{:.7f}</font><font size=\"2\"> MHz</font>"
|
||||
.format(frequency/1e6))
|
||||
|
||||
def sort_key(self):
|
||||
|
@ -13,6 +13,17 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Segment:
|
||||
"""Serialize the lines for a single Segment.
|
||||
|
||||
Attributes:
|
||||
max_time (int): Maximum duration of a line.
|
||||
max_val (int): Maximum absolute value (scale) of the DAC output.
|
||||
max_out (float): Output voltage at :attr:`max_val`. In Volt.
|
||||
out_scale (float): Steps per Volt.
|
||||
cordic_gain (float): CORDIC amplitude gain.
|
||||
addr (int): Address assigned to this segment.
|
||||
data (bytes): Serialized segment data.
|
||||
"""
|
||||
max_time = 1 << 16 # uint16 timer
|
||||
max_val = 1 << 15 # int16 DAC
|
||||
max_out = 10. # Volt
|
||||
@ -27,6 +38,24 @@ class Segment:
|
||||
|
||||
def line(self, typ, duration, data, trigger=False, silence=False,
|
||||
aux=False, shift=0, jump=False, clear=False, wait=False):
|
||||
"""Append a line to this segment.
|
||||
|
||||
Args:
|
||||
typ (int): Output module to target with this line.
|
||||
duration (int): Duration of the line in units of
|
||||
``clock_period*2**shift``.
|
||||
data (bytes): Opaque data for the output module.
|
||||
trigger (bool): Wait for trigger assertion before executing
|
||||
this line.
|
||||
silence (bool): Disable DAC clocks for the duration of this line.
|
||||
aux (bool): Assert the AUX (F5 TTL) output during this line.
|
||||
shift (int): Duration and spline evolution exponent.
|
||||
jump (bool): Return to the frame address table after this line.
|
||||
clear (bool): Clear the DDS phase accumulator when starting to
|
||||
exectute this line.
|
||||
wait (bool): Wait for trigger assertion before executing the next
|
||||
line.
|
||||
"""
|
||||
assert len(data) % 2 == 0, data
|
||||
assert len(data)//2 <= 14
|
||||
# assert dt*(1 << shift) > 1 + len(data)//2
|
||||
@ -39,6 +68,15 @@ class Segment:
|
||||
|
||||
@staticmethod
|
||||
def pack(widths, values):
|
||||
"""Pack spline data.
|
||||
|
||||
Args:
|
||||
widths (list[int]): Widths of values in multiples of 16 bits.
|
||||
values (list[int]): Values to pack.
|
||||
|
||||
Returns:
|
||||
data (bytes): Packed data.
|
||||
"""
|
||||
fmt = "<"
|
||||
ud = []
|
||||
for width, value in zip(widths, values):
|
||||
@ -60,7 +98,11 @@ class Segment:
|
||||
def bias(self, amplitude=[], **kwargs):
|
||||
"""Append a bias line to this segment.
|
||||
|
||||
Amplitude in volts
|
||||
Args:
|
||||
amplitude (list[float]): Amplitude coefficients in in Volts and
|
||||
increasing powers of ``1/(2**shift*clock_period)``.
|
||||
Discrete time compensation will be applied.
|
||||
**kwargs: Passed to :meth:`line`.
|
||||
"""
|
||||
coef = [self.out_scale*a for a in amplitude]
|
||||
discrete_compensate(coef)
|
||||
@ -68,12 +110,18 @@ class Segment:
|
||||
self.line(typ=0, data=data, **kwargs)
|
||||
|
||||
def dds(self, amplitude=[], phase=[], **kwargs):
|
||||
"""Append a dds line to this segment.
|
||||
"""Append a DDS line to this segment.
|
||||
|
||||
Amplitude in volts,
|
||||
phase[0] in turns,
|
||||
phase[1] in turns*sample_rate,
|
||||
phase[2] in turns*(sample_rate/2**shift)**2
|
||||
Args:
|
||||
amplitude (list[float]): Amplitude coefficients in in Volts and
|
||||
increasing powers of ``1/(2**shift*clock_period)``.
|
||||
Discrete time compensation and CORDIC gain compensation
|
||||
will be applied by this method.
|
||||
phase (list[float]): Phase/frequency/chirp coefficients.
|
||||
``phase[0]`` in ``turns``,
|
||||
``phase[1]`` in ``turns/clock_period``,
|
||||
``phase[2]`` in ``turns/(clock_period**2*2**shift)``.
|
||||
**kwargs: Passed to :meth:`line`.
|
||||
"""
|
||||
scale = self.out_scale/self.cordic_gain
|
||||
coef = [scale*a for a in amplitude]
|
||||
@ -86,6 +134,13 @@ class Segment:
|
||||
|
||||
|
||||
class Channel:
|
||||
"""PDQ2 Channel.
|
||||
|
||||
Attributes:
|
||||
num_frames (int): Number of frames supported.
|
||||
max_data (int): Number of 16 bit data words per channel.
|
||||
segments (list[Segment]): Segments added to this channel.
|
||||
"""
|
||||
num_frames = 8
|
||||
max_data = 4*(1 << 10) # 8kx16 8kx16 4kx16
|
||||
|
||||
@ -93,14 +148,27 @@ class Channel:
|
||||
self.segments = []
|
||||
|
||||
def clear(self):
|
||||
"""Remove all segments."""
|
||||
self.segments.clear()
|
||||
|
||||
def new_segment(self):
|
||||
"""Create and attach a new :class:`Segment` to this channel.
|
||||
|
||||
Returns:
|
||||
:class:`Segment`
|
||||
"""
|
||||
segment = Segment()
|
||||
self.segments.append(segment)
|
||||
return segment
|
||||
|
||||
def place(self):
|
||||
"""Place segments contiguously.
|
||||
|
||||
Assign segment start addresses and determine length of data.
|
||||
|
||||
Returns:
|
||||
addr (int): Amount of memory in use on this channel.
|
||||
"""
|
||||
addr = self.num_frames
|
||||
for segment in self.segments:
|
||||
segment.addr = addr
|
||||
@ -109,6 +177,23 @@ class Channel:
|
||||
return addr
|
||||
|
||||
def table(self, entry=None):
|
||||
"""Generate the frame address table.
|
||||
|
||||
Unused frame indices are assigned the zero address in the frame address
|
||||
table.
|
||||
This will cause the memory parser to remain in the frame address table
|
||||
until another frame is selected.
|
||||
|
||||
The frame entry segments can be any segments in the channel.
|
||||
|
||||
Args:
|
||||
entry (list[Segment]): List of initial segments for each frame.
|
||||
If not specified, the first :attr:`num_frames` segments are
|
||||
used as frame entry points.
|
||||
|
||||
Returns:
|
||||
table (bytes): Frame address table.
|
||||
"""
|
||||
table = [0] * self.num_frames
|
||||
if entry is None:
|
||||
entry = self.segments
|
||||
@ -118,6 +203,18 @@ class Channel:
|
||||
return struct.pack("<" + "H"*self.num_frames, *table)
|
||||
|
||||
def serialize(self, entry=None):
|
||||
"""Serialize the memory for this channel.
|
||||
|
||||
Places the segments contiguously in memory after the frame table.
|
||||
Allocates and assigns segment and frame table addresses.
|
||||
Serializes segment data and prepends frame address table.
|
||||
|
||||
Args:
|
||||
entry (list[Segment]): See :meth:`table`.
|
||||
|
||||
Returns:
|
||||
data (bytes): Channel memory data.
|
||||
"""
|
||||
self.place()
|
||||
data = b"".join([segment.data for segment in self.segments])
|
||||
return self.table(entry) + data
|
||||
@ -125,7 +222,22 @@ class Channel:
|
||||
|
||||
class Pdq2:
|
||||
"""
|
||||
PDQ DAC (a.k.a. QC_Waveform)
|
||||
PDQ stack.
|
||||
|
||||
Args:
|
||||
url (str): Pyserial device URL. Can be ``hwgrep://`` style
|
||||
(search for serial number, bus topology, USB VID:PID combination),
|
||||
``COM15`` for a Windows COM port number,
|
||||
``/dev/ttyUSB0`` for a Linux serial port.
|
||||
dev (file-like): File handle to use as device. If passed, ``url`` is
|
||||
ignored.
|
||||
num_boards (int): Number of boards in this stack.
|
||||
|
||||
Attributes:
|
||||
num_dacs (int): Number of DAC outputs per board.
|
||||
num_channels (int): Number of channels in this stack.
|
||||
num_boards (int): Number of boards in this stack.
|
||||
channels (list[Channel]): List of :class:`Channel` in this stack.
|
||||
"""
|
||||
num_dacs = 3
|
||||
freq = 50e6
|
||||
@ -154,42 +266,58 @@ class Pdq2:
|
||||
self.freq = float(freq)
|
||||
|
||||
def close(self):
|
||||
"""Close the USB device handle."""
|
||||
self.dev.close()
|
||||
del self.dev
|
||||
|
||||
def write(self, data):
|
||||
"""Write data to the PDQ2 board.
|
||||
|
||||
Args:
|
||||
data (bytes): Data to write.
|
||||
"""
|
||||
logger.debug("> %r", data)
|
||||
written = self.dev.write(data)
|
||||
if isinstance(written, int):
|
||||
assert written == len(data)
|
||||
|
||||
def cmd(self, cmd, enable):
|
||||
"""Execute a command.
|
||||
|
||||
Args:
|
||||
cmd (str): Command to execute. One of (``RESET``, ``TRIGGER``,
|
||||
``ARM``, ``DCM``, ``START``).
|
||||
enable (bool): Enable (``True``) or disable (``False``) the
|
||||
feature.
|
||||
"""
|
||||
cmd = self._commands.index(cmd) << 1
|
||||
if not enable:
|
||||
cmd |= 1
|
||||
self.write(struct.pack("cb", self._escape, cmd))
|
||||
|
||||
def write_mem(self, channel, data, start_addr=0):
|
||||
"""Write to channel memory.
|
||||
|
||||
Args:
|
||||
channel (int): Channel index to write to. Assumes every board in
|
||||
the stack has :attr:`num_dacs` DAC outputs.
|
||||
data (bytes): Data to write to memory.
|
||||
start_addr (int): Start address to write data to.
|
||||
"""
|
||||
board, dac = divmod(channel, self.num_dacs)
|
||||
data = struct.pack("<HHH", (board << 4) | dac, start_addr,
|
||||
start_addr + len(data)//2 - 1) + data
|
||||
data = data.replace(self._escape, self._escape + self._escape)
|
||||
self.write(data)
|
||||
|
||||
def flush(self):
|
||||
self.dev.flush()
|
||||
|
||||
def park(self):
|
||||
self.cmd("START", False)
|
||||
self.cmd("TRIGGER", True)
|
||||
self.flush()
|
||||
|
||||
def unpark(self):
|
||||
self.cmd("TRIGGER", False)
|
||||
self.cmd("START", True)
|
||||
self.flush()
|
||||
|
||||
def program_segments(self, segments, data):
|
||||
"""Append the wavesynth lines to the given segments.
|
||||
|
||||
Args:
|
||||
segments (list[Segment]): List of :class:`Segment` to append the
|
||||
lines to.
|
||||
data (list): List of wavesynth lines.
|
||||
"""
|
||||
for i, line in enumerate(data):
|
||||
dac_divider = line.get("dac_divider", 1)
|
||||
shift = int(log(dac_divider, 2))
|
||||
@ -208,6 +336,25 @@ class Pdq2:
|
||||
silence=silence, **target_data)
|
||||
|
||||
def program(self, program, channels=None):
|
||||
"""Serialize a wavesynth program and write it to the channels
|
||||
in the stack.
|
||||
|
||||
The :class:`Channel` targeted are cleared and each frame in the
|
||||
wavesynth program is appended to a fresh set of :class:`Segment`
|
||||
of the channels. All segments are allocated, the frame address tale
|
||||
is generated, the channels are serialized and their memories are
|
||||
written.
|
||||
|
||||
Short single-cycle lines are prepended and appended to each frame to
|
||||
allow proper write interlocking and to assure that the memory reader
|
||||
can be reliably parked in the frame address table.
|
||||
The first line of each frame is mandatorily triggered.
|
||||
|
||||
Args:
|
||||
program (list): Wavesynth program to serialize.
|
||||
channels (list[int]): Channel indices to use. If unspecified, all
|
||||
channels are used.
|
||||
"""
|
||||
if channels is None:
|
||||
channels = range(self.num_channels)
|
||||
chs = [self.channels[i] for i in channels]
|
||||
@ -225,5 +372,18 @@ class Pdq2:
|
||||
for channel, ch in zip(channels, chs):
|
||||
self.write_mem(channel, ch.serialize())
|
||||
|
||||
def flush(self):
|
||||
self.dev.flush()
|
||||
|
||||
def park(self):
|
||||
self.cmd("START", False)
|
||||
self.cmd("TRIGGER", True)
|
||||
self.flush()
|
||||
|
||||
def unpark(self):
|
||||
self.cmd("TRIGGER", False)
|
||||
self.cmd("START", True)
|
||||
self.flush()
|
||||
|
||||
def ping(self):
|
||||
return True
|
||||
|
@ -52,14 +52,13 @@ class FloppingF(EnvExperiment):
|
||||
self.mutate_dataset("flopping_f_frequency", i, f)
|
||||
self.mutate_dataset("flopping_f_brightness", i, m_brightness)
|
||||
time.sleep(0.1)
|
||||
self.scheduler.submit(self.scheduler.pipeline_name, self.scheduler.expid,
|
||||
self.scheduler.priority, time.time() + 20, False)
|
||||
self.scheduler.submit(due_date=time.time() + 20)
|
||||
|
||||
def analyze(self):
|
||||
# Use get_dataset so that analyze can be run stand-alone.
|
||||
brightness = self.get_dataset("flopping_f_brightness")
|
||||
try:
|
||||
frequency = self.get_dataset("flopping_f_frequency")
|
||||
frequency = self.get_dataset("flopping_f_frequency", archive=False)
|
||||
except KeyError:
|
||||
# Since flopping_f_frequency is not saved, it is missing if
|
||||
# analyze() is run on HDF5 data. But assuming that the arguments
|
||||
@ -69,7 +68,8 @@ class FloppingF(EnvExperiment):
|
||||
self.set_dataset("flopping_f_frequency", frequency,
|
||||
broadcast=True, save=False)
|
||||
popt, pcov = curve_fit(model, frequency, brightness,
|
||||
p0=[self.get_dataset("flopping_freq", 1500.0)])
|
||||
p0=[self.get_dataset("flopping_freq", 1500.0,
|
||||
archive=False)])
|
||||
perr = np.sqrt(np.diag(pcov))
|
||||
if perr < 0.1:
|
||||
F0 = float(popt)
|
||||
|
@ -55,7 +55,7 @@ def main():
|
||||
|
||||
object_map, kernel_library, _, _ = \
|
||||
core.compile(exp.run, [exp_inst], {},
|
||||
with_attr_writeback=False)
|
||||
attribute_writeback=False)
|
||||
except CompileError as error:
|
||||
return
|
||||
finally:
|
||||
|
154
artiq/frontend/artiq_devtool.py
Normal file
154
artiq/frontend/artiq_devtool.py
Normal file
@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env python3.5
|
||||
|
||||
# This script makes the following assumptions:
|
||||
# * miniconda is installed remotely at ~/miniconda
|
||||
# * misoc and artiq are installed remotely via conda
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
import subprocess
|
||||
import socket
|
||||
import select
|
||||
import threading
|
||||
import paramiko
|
||||
|
||||
from artiq.tools import verbosity_args, init_logger, logger
|
||||
from random import Random
|
||||
|
||||
|
||||
def get_argparser():
|
||||
parser = argparse.ArgumentParser(description="ARTIQ core device "
|
||||
"development tool")
|
||||
|
||||
verbosity_args(parser)
|
||||
|
||||
parser.add_argument("--host", nargs=1, metavar="HOST",
|
||||
type=str, default="lab.m-labs.hk",
|
||||
help="SSH host where the development board is located")
|
||||
parser.add_argument("--serial", nargs=1, metavar="SERIAL",
|
||||
type=str, default="/dev/ttyUSB0",
|
||||
help="TTY device corresponding to the development board")
|
||||
parser.add_argument("--ip", nargs=1, metavar="IP",
|
||||
type=str, default="kc705.lab.m-labs.hk",
|
||||
help="IP address corresponding to the development board")
|
||||
|
||||
parser.add_argument("actions", metavar="ACTION",
|
||||
type=str, default=[], nargs="+",
|
||||
help="actions to perform (sequence of: build boot connect)")
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def main():
|
||||
args = get_argparser().parse_args()
|
||||
init_logger(args)
|
||||
|
||||
ssh = None
|
||||
def get_ssh():
|
||||
nonlocal ssh
|
||||
if ssh is not None:
|
||||
return ssh
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.load_system_host_keys()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
ssh.connect(args.host)
|
||||
return ssh
|
||||
|
||||
sftp = None
|
||||
def get_sftp():
|
||||
nonlocal sftp
|
||||
if sftp is not None:
|
||||
return sftp
|
||||
sftp = get_ssh().open_sftp()
|
||||
return sftp
|
||||
|
||||
rng = Random()
|
||||
tmp = "artiq" + "".join([rng.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ") for _ in range(6)])
|
||||
env = "bash -c 'export PATH=$HOME/miniconda/bin:$PATH; exec $0 $*' "
|
||||
|
||||
def run_command(cmd):
|
||||
logger.info("Executing {}".format(cmd))
|
||||
chan = get_ssh().get_transport().open_session()
|
||||
chan.set_combine_stderr(True)
|
||||
chan.exec_command(cmd.format(tmp=tmp, env=env, serial=args.serial, ip=args.ip))
|
||||
return chan.makefile()
|
||||
|
||||
def drain(chan):
|
||||
while True:
|
||||
char = chan.read(1)
|
||||
if char == b"":
|
||||
break
|
||||
sys.stderr.write(char.decode("utf-8"))
|
||||
|
||||
for action in args.actions:
|
||||
if action == "build":
|
||||
logger.info("Building runtime")
|
||||
try:
|
||||
subprocess.check_call(["python3", "-m", "artiq.gateware.targets.kc705",
|
||||
"-H", "nist_clock",
|
||||
"--no-compile-gateware",
|
||||
"--output-dir", "/tmp/kc705"])
|
||||
except subprocess.CalledProcessError:
|
||||
logger.error("Build failed")
|
||||
sys.exit(1)
|
||||
|
||||
elif action == "boot":
|
||||
logger.info("Uploading runtime")
|
||||
get_sftp().mkdir("/tmp/{tmp}".format(tmp=tmp))
|
||||
get_sftp().put("/tmp/kc705/software/runtime/runtime.bin",
|
||||
"/tmp/{tmp}/runtime.bin".format(tmp=tmp))
|
||||
|
||||
logger.info("Booting runtime")
|
||||
flterm = run_command(
|
||||
"{env} python3 flterm.py {serial} " +
|
||||
"--kernel /tmp/{tmp}/runtime.bin --upload-only")
|
||||
artiq_flash = run_command(
|
||||
"{env} artiq_flash start")
|
||||
drain(flterm)
|
||||
|
||||
elif action == "connect":
|
||||
def forwarder(port):
|
||||
listener = socket.socket()
|
||||
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
listener.bind(('localhost', port))
|
||||
listener.listen(1)
|
||||
while True:
|
||||
local_stream, peer_addr = listener.accept()
|
||||
logger.info("Accepting %s:%s and opening SSH channel to %s:%s",
|
||||
*peer_addr, args.ip, port)
|
||||
remote_stream = get_ssh().get_transport() \
|
||||
.open_channel('direct-tcpip', (args.ip, port), peer_addr)
|
||||
while True:
|
||||
try:
|
||||
r, w, x = select.select([local_stream, remote_stream], [], [])
|
||||
if local_stream in r:
|
||||
data = local_stream.recv(1024)
|
||||
if data == b"":
|
||||
break
|
||||
remote_stream.send(data)
|
||||
if remote_stream in r:
|
||||
data = remote_stream.recv(1024)
|
||||
if data == b"":
|
||||
break
|
||||
local_stream.send(data)
|
||||
except Exception as e:
|
||||
logger.exception("Forward error on port %s", port)
|
||||
local_stream.close()
|
||||
remote_stream.close()
|
||||
|
||||
for port in (1381, 1382):
|
||||
thread = threading.Thread(target=forwarder, args=(port,),
|
||||
name="port-{}".format(port), daemon=True)
|
||||
thread.start()
|
||||
|
||||
logger.info("Connecting to device")
|
||||
flterm = run_command(
|
||||
"{env} python3 flterm.py {serial} --output-only")
|
||||
drain(flterm)
|
||||
|
||||
else:
|
||||
logger.error("Unknown action {}".format(action))
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -73,8 +73,7 @@ class LLVMIRRunner(FileRunner):
|
||||
with open(self.file, "r") as f:
|
||||
llmodule = llvm.parse_assembly(f.read())
|
||||
llmodule.verify()
|
||||
return self.target.link([self.target.assemble(llmodule)],
|
||||
init_fn="__modinit__")
|
||||
return self.target.link([self.target.assemble(llmodule)])
|
||||
|
||||
|
||||
class LLVMBitcodeRunner(FileRunner):
|
||||
@ -82,8 +81,7 @@ class LLVMBitcodeRunner(FileRunner):
|
||||
with open(self.file, "rb") as f:
|
||||
llmodule = llvm.parse_bitcode(f.read())
|
||||
llmodule.verify()
|
||||
return self.target.link([self.target.assemble(llmodule)],
|
||||
init_fn="__modinit__")
|
||||
return self.target.link([self.target.assemble(llmodule)])
|
||||
|
||||
|
||||
class DummyScheduler:
|
||||
@ -95,7 +93,7 @@ class DummyScheduler:
|
||||
|
||||
self._next_rid = 1
|
||||
|
||||
def submit(self, pipeline_name, expid, priority, due_date, flush):
|
||||
def submit(self, pipeline_name=None, expid=None, priority=None, due_date=None, flush=False):
|
||||
rid = self._next_rid
|
||||
self._next_rid += 1
|
||||
logger.info("Submitting: %s, RID=%s", expid, rid)
|
||||
|
@ -7,7 +7,7 @@ from misoc.integration.soc_core import mem_decoder
|
||||
|
||||
class KernelCPU(Module):
|
||||
def __init__(self, platform,
|
||||
exec_address=0x42000000,
|
||||
exec_address=0x40800000,
|
||||
main_mem_origin=0x40000000,
|
||||
l2_size=8192):
|
||||
self._reset = CSRStorage(reset=1)
|
||||
|
@ -3,19 +3,19 @@ from misoc.interconnect import wishbone
|
||||
|
||||
|
||||
class Mailbox(Module):
|
||||
def __init__(self):
|
||||
def __init__(self, size=1):
|
||||
self.i1 = wishbone.Interface()
|
||||
self.i2 = wishbone.Interface()
|
||||
|
||||
# # #
|
||||
|
||||
value = Signal(32)
|
||||
values = Array([Signal(32) for _ in range(size)])
|
||||
for i in self.i1, self.i2:
|
||||
self.sync += [
|
||||
i.dat_r.eq(value),
|
||||
i.dat_r.eq(values[i.adr[:bits_for(size-1)]]),
|
||||
i.ack.eq(0),
|
||||
If(i.cyc & i.stb & ~i.ack,
|
||||
i.ack.eq(1),
|
||||
If(i.we, value.eq(i.dat_w))
|
||||
If(i.we, values[i.adr[:bits_for(size-1)]].eq(i.dat_w))
|
||||
)
|
||||
]
|
||||
|
@ -1,7 +1,7 @@
|
||||
from migen import *
|
||||
from misoc.interconnect.stream import Endpoint
|
||||
|
||||
from misoc.cores.cordic import Cordic
|
||||
|
||||
from .accu import PhasedAccu, Accu
|
||||
from .tools import eqh, Delay
|
||||
from .spline import Spline
|
||||
|
@ -13,10 +13,10 @@ fmc_adapter_io = [
|
||||
Subsignal("mosi", Pins("HPC:LA03_N")),
|
||||
Subsignal("miso", Pins("HPC:LA04_P")),
|
||||
Subsignal("en", Pins("HPC:LA05_N")),
|
||||
IOStandard("LVTTL"),
|
||||
IOStandard("LVCMOS25"),
|
||||
),
|
||||
("ad9154_txen", 0, Pins("HPC:LA07_P"), IOStandard("LVTTL")),
|
||||
("ad9154_txen", 1, Pins("HPC:LA07_N"), IOStandard("LVTTL")),
|
||||
("ad9154_txen", 0, Pins("HPC:LA07_P"), IOStandard("LVCMOS25")),
|
||||
("ad9154_txen", 1, Pins("HPC:LA07_N"), IOStandard("LVCMOS25")),
|
||||
("ad9154_refclk", 0,
|
||||
Subsignal("p", Pins("HPC:GBTCLK0_M2C_P")),
|
||||
Subsignal("n", Pins("HPC:GBTCLK0_M2C_N")),
|
||||
@ -25,16 +25,19 @@ fmc_adapter_io = [
|
||||
Subsignal("p", Pins("HPC:LA00_CC_P")),
|
||||
Subsignal("n", Pins("HPC:LA00_CC_N")),
|
||||
IOStandard("LVDS_25"),
|
||||
Misc("DIFF_TERM=TRUE"),
|
||||
),
|
||||
("ad9154_sync", 0,
|
||||
Subsignal("p", Pins("HPC:LA01_CC_P")),
|
||||
Subsignal("n", Pins("HPC:LA01_CC_N")),
|
||||
IOStandard("LVDS_25"),
|
||||
Misc("DIFF_TERM=TRUE"),
|
||||
),
|
||||
("ad9154_sync", 1,
|
||||
Subsignal("p", Pins("HPC:LA02_P")),
|
||||
Subsignal("n", Pins("HPC:LA02_N")),
|
||||
IOStandard("LVDS_25"),
|
||||
Misc("DIFF_TERM=TRUE"),
|
||||
),
|
||||
("ad9154_jesd", 0, # AD9154's SERIND7
|
||||
Subsignal("txp", Pins("HPC:DP0_C2M_P")),
|
||||
|
@ -24,10 +24,8 @@ class _GrayCodeTransfer(Module):
|
||||
self.sync.rtio += value_gray_rtio.eq(self.i ^ self.i[1:])
|
||||
# transfer to system clock domain
|
||||
value_gray_sys = Signal(width)
|
||||
self.specials += [
|
||||
NoRetiming(value_gray_rtio),
|
||||
MultiReg(value_gray_rtio, value_gray_sys)
|
||||
]
|
||||
value_gray_rtio.attr.add("no_retiming")
|
||||
self.specials += MultiReg(value_gray_rtio, value_gray_sys)
|
||||
# convert back to binary
|
||||
value_sys = Signal(width)
|
||||
self.comb += value_sys[-1].eq(value_gray_sys[-1])
|
||||
|
@ -28,7 +28,7 @@ class AMPSoC:
|
||||
self.add_cpulevel_sdram_if(self.kernel_cpu.wb_sdram)
|
||||
self.csr_devices.append("kernel_cpu")
|
||||
|
||||
self.submodules.mailbox = amp.Mailbox()
|
||||
self.submodules.mailbox = amp.Mailbox(size=3)
|
||||
self.add_wb_slave(mem_decoder(self.mem_map["mailbox"]),
|
||||
self.mailbox.i1)
|
||||
self.kernel_cpu.add_wb_slave(mem_decoder(self.mem_map["mailbox"]),
|
||||
|
@ -8,7 +8,6 @@ from migen.genlib.cdc import MultiReg
|
||||
from migen.build.generic_platform import *
|
||||
from migen.build.xilinx.vivado import XilinxVivadoToolchain
|
||||
from migen.build.xilinx.ise import XilinxISEToolchain
|
||||
from migen.fhdl.specials import Keep
|
||||
from migen.genlib.io import DifferentialInput
|
||||
|
||||
from jesd204b.common import (JESD204BTransportSettings,
|
||||
@ -149,10 +148,10 @@ class _NIST_Ions(MiniSoC, AMPSoC):
|
||||
self.register_kernel_cpu_csrdevice("i2c")
|
||||
self.config["I2C_BUS_COUNT"] = 1
|
||||
|
||||
def add_rtio(self, rtio_channels, rtio_crg=None):
|
||||
if rtio_crg is None:
|
||||
rtio_crg = _RTIOCRG(self.platform, self.crg.cd_sys.clk)
|
||||
self.submodules.rtio_crg = rtio_crg
|
||||
self.config["HAS_DDS"] = None
|
||||
|
||||
def add_rtio(self, rtio_channels):
|
||||
self.submodules.rtio_crg = _RTIOCRG(self.platform, self.crg.cd_sys.clk)
|
||||
self.csr_devices.append("rtio_crg")
|
||||
self.submodules.rtio = rtio.RTIO(rtio_channels)
|
||||
self.register_kernel_cpu_csrdevice("rtio")
|
||||
@ -160,22 +159,11 @@ class _NIST_Ions(MiniSoC, AMPSoC):
|
||||
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
|
||||
self.csr_devices.append("rtio_moninj")
|
||||
|
||||
self.specials += [
|
||||
Keep(self.rtio.cd_rsys.clk),
|
||||
Keep(self.rtio_crg.cd_rtio.clk),
|
||||
Keep(self.ethphy.crg.cd_eth_rx.clk),
|
||||
Keep(self.ethphy.crg.cd_eth_tx.clk),
|
||||
]
|
||||
|
||||
self.platform.add_period_constraint(self.rtio.cd_rsys.clk, 8.)
|
||||
self.rtio_crg.cd_rtio.clk.attr.add("keep")
|
||||
self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.)
|
||||
self.platform.add_period_constraint(self.ethphy.crg.cd_eth_rx.clk, 8.)
|
||||
self.platform.add_period_constraint(self.ethphy.crg.cd_eth_tx.clk, 8.)
|
||||
self.platform.add_false_path_constraints(
|
||||
self.rtio.cd_rsys.clk,
|
||||
self.rtio_crg.cd_rtio.clk,
|
||||
self.ethphy.crg.cd_eth_rx.clk,
|
||||
self.ethphy.crg.cd_eth_tx.clk)
|
||||
self.crg.cd_sys.clk,
|
||||
self.rtio_crg.cd_rtio.clk)
|
||||
|
||||
self.submodules.rtio_analyzer = rtio.Analyzer(self.rtio,
|
||||
self.get_native_sdram_if())
|
||||
@ -223,7 +211,7 @@ class NIST_QC1(_NIST_Ions):
|
||||
self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels)
|
||||
self.config["RTIO_DDS_COUNT"] = 1
|
||||
self.config["DDS_CHANNELS_PER_BUS"] = 8
|
||||
self.config["DDS_AD9858"] = True
|
||||
self.config["DDS_AD9858"] = None
|
||||
phy = dds.AD9858(platform.request("dds"), 8)
|
||||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel.from_phy(phy,
|
||||
@ -297,8 +285,8 @@ class NIST_CLOCK(_NIST_Ions):
|
||||
self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels)
|
||||
self.config["RTIO_DDS_COUNT"] = 1
|
||||
self.config["DDS_CHANNELS_PER_BUS"] = 11
|
||||
self.config["DDS_AD9914"] = True
|
||||
self.config["DDS_ONEHOT_SEL"] = True
|
||||
self.config["DDS_AD9914"] = None
|
||||
self.config["DDS_ONEHOT_SEL"] = None
|
||||
phy = dds.AD9914(platform.request("dds"), 11, onehot=True)
|
||||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel.from_phy(phy,
|
||||
@ -375,8 +363,8 @@ class NIST_QC2(_NIST_Ions):
|
||||
self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels)
|
||||
self.config["RTIO_DDS_COUNT"] = 2
|
||||
self.config["DDS_CHANNELS_PER_BUS"] = 12
|
||||
self.config["DDS_AD9914"] = True
|
||||
self.config["DDS_ONEHOT_SEL"] = True
|
||||
self.config["DDS_AD9914"] = None
|
||||
self.config["DDS_ONEHOT_SEL"] = None
|
||||
for backplane_offset in range(2):
|
||||
phy = dds.AD9914(
|
||||
platform.request("dds", backplane_offset), 12, onehot=True)
|
||||
@ -394,11 +382,10 @@ class NIST_QC2(_NIST_Ions):
|
||||
|
||||
|
||||
class _PhaserCRG(Module, AutoCSR):
|
||||
def __init__(self, platform, rtio_internal_clk):
|
||||
def __init__(self, platform, refclk):
|
||||
self._clock_sel = CSRStorage()
|
||||
self._pll_reset = CSRStorage(reset=1)
|
||||
self._pll_locked = CSRStatus()
|
||||
self.refclk = Signal()
|
||||
self.clock_domains.cd_rtio = ClockDomain()
|
||||
self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True)
|
||||
|
||||
@ -411,11 +398,11 @@ class _PhaserCRG(Module, AutoCSR):
|
||||
|
||||
p_REF_JITTER1=0.01, p_REF_JITTER2=0.01,
|
||||
p_CLKIN1_PERIOD=4.0, p_CLKIN2_PERIOD=4.0,
|
||||
i_CLKIN1=0, i_CLKIN2=self.refclk,
|
||||
i_CLKIN1=0, i_CLKIN2=refclk,
|
||||
# Warning: CLKINSEL=0 means CLKIN2 is selected
|
||||
i_CLKINSEL=~self._clock_sel.storage,
|
||||
|
||||
# VCO @ 1GHz when using 125MHz input
|
||||
# VCO @ 1GHz when using 250MHz input
|
||||
p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=2,
|
||||
i_CLKFBIN=self.cd_rtio.clk,
|
||||
i_RST=self._pll_reset.storage,
|
||||
@ -431,6 +418,8 @@ class _PhaserCRG(Module, AutoCSR):
|
||||
MultiReg(pll_locked | ~self._clock_sel.storage,
|
||||
self._pll_locked.status)
|
||||
]
|
||||
self.cd_rtio.clk.attr.add("keep")
|
||||
platform.add_period_constraint(self.cd_rtio.clk, 8.)
|
||||
|
||||
|
||||
class AD9154JESD(Module, AutoCSR):
|
||||
@ -444,28 +433,29 @@ class AD9154JESD(Module, AutoCSR):
|
||||
|
||||
sync_pads = platform.request("ad9154_sync")
|
||||
self.jsync = Signal()
|
||||
self.refclk = Signal()
|
||||
self.specials += DifferentialInput(
|
||||
sync_pads.p, sync_pads.n, self.jsync)
|
||||
|
||||
refclk = Signal()
|
||||
self.clock_domains.cd_jesd = ClockDomain()
|
||||
refclk_pads = platform.request("ad9154_refclk")
|
||||
platform.add_period_constraint(refclk_pads.p, 1e9/refclk_freq)
|
||||
|
||||
self.specials += [
|
||||
Instance("IBUFDS_GTE2", i_CEB=0,
|
||||
i_I=refclk_pads.p, i_IB=refclk_pads.n, o_O=self.refclk),
|
||||
Instance("BUFG", i_I=self.refclk, o_O=self.cd_jesd.clk),
|
||||
i_I=refclk_pads.p, i_IB=refclk_pads.n, o_O=refclk),
|
||||
Instance("BUFG", i_I=refclk, o_O=self.cd_jesd.clk),
|
||||
AsyncResetSynchronizer(self.cd_jesd, ResetSignal("rio_phy")),
|
||||
]
|
||||
self.cd_jesd.clk.attr.add("keep")
|
||||
platform.add_period_constraint(self.cd_jesd.clk, 1e9/refclk_freq)
|
||||
|
||||
qpll = GTXQuadPLL(self.refclk, refclk_freq, linerate)
|
||||
qpll = GTXQuadPLL(refclk, refclk_freq, linerate)
|
||||
self.submodules += qpll
|
||||
phys = []
|
||||
for i in range(4):
|
||||
phy = JESD204BPhyTX(
|
||||
qpll, platform.request("ad9154_jesd", i), fabric_freq)
|
||||
phy.gtx.cd_tx.clk.attr.add("keep")
|
||||
platform.add_period_constraint(phy.gtx.cd_tx.clk, 40*1e9/linerate)
|
||||
platform.add_false_path_constraints(self.cd_jesd.clk,
|
||||
phy.gtx.cd_tx.clk)
|
||||
@ -484,16 +474,15 @@ class AD9154JESD(Module, AutoCSR):
|
||||
|
||||
# blinking leds for transceiver reset status
|
||||
for i in range(4):
|
||||
led = platform.request("user_led", 4 + i)
|
||||
counter = Signal(max=fabric_freq//2 + 1)
|
||||
counter = Signal(max=fabric_freq)
|
||||
self.comb += platform.request("user_led", 4 + i).eq(counter[-1])
|
||||
sync = getattr(self.sync, "phy{}_tx".format(i))
|
||||
sync += \
|
||||
sync += [
|
||||
counter.eq(counter - 1),
|
||||
If(counter == 0,
|
||||
led.eq(~led),
|
||||
counter.eq(fabric_freq//2)
|
||||
).Else(
|
||||
counter.eq(counter - 1)
|
||||
counter.eq(fabric_freq - 1)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class AD9154(Module, AutoCSR):
|
||||
@ -517,18 +506,43 @@ class AD9154(Module, AutoCSR):
|
||||
self.sync.jesd += conv.eq(Mux(z, Cat(ch.o[:2]), Cat(ch.o[2:])))
|
||||
|
||||
|
||||
class Phaser(_NIST_Ions):
|
||||
class Phaser(MiniSoC, AMPSoC):
|
||||
mem_map = {
|
||||
"ad9154": 0x50000000,
|
||||
"timer_kernel": 0x10000000, # (shadow @0x90000000)
|
||||
"rtio": 0x20000000, # (shadow @0xa0000000)
|
||||
"i2c": 0x30000000, # (shadow @0xb0000000)
|
||||
"mailbox": 0x70000000, # (shadow @0xf0000000)
|
||||
"ad9154": 0x50000000,
|
||||
}
|
||||
mem_map.update(_NIST_Ions.mem_map)
|
||||
mem_map.update(MiniSoC.mem_map)
|
||||
|
||||
def __init__(self, cpu_type="or1k", **kwargs):
|
||||
_NIST_Ions.__init__(self, cpu_type, **kwargs)
|
||||
MiniSoC.__init__(self,
|
||||
cpu_type=cpu_type,
|
||||
sdram_controller_type="minicon",
|
||||
l2_size=128*1024,
|
||||
with_timer=False,
|
||||
ident=artiq_version,
|
||||
**kwargs)
|
||||
AMPSoC.__init__(self)
|
||||
self.platform.toolchain.bitstream_commands.extend([
|
||||
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
|
||||
])
|
||||
|
||||
platform = self.platform
|
||||
platform.add_extension(_ams101_dac)
|
||||
platform.add_extension(phaser.fmc_adapter_io)
|
||||
|
||||
self.submodules.leds = gpio.GPIOOut(Cat(
|
||||
platform.request("user_led", 0),
|
||||
platform.request("user_led", 1)))
|
||||
self.csr_devices.append("leds")
|
||||
|
||||
i2c = platform.request("i2c")
|
||||
self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
||||
self.register_kernel_cpu_csrdevice("i2c")
|
||||
self.config["I2C_BUS_COUNT"] = 1
|
||||
|
||||
self.submodules.ad9154 = AD9154(platform)
|
||||
self.register_kernel_cpu_csrdevice("ad9154")
|
||||
self.config["AD9154_DAC_CS"] = 1 << 0
|
||||
@ -537,7 +551,7 @@ class Phaser(_NIST_Ions):
|
||||
rtio_channels = []
|
||||
|
||||
phy = ttl_serdes_7series.Inout_8X(
|
||||
platform.request("user_sma_gpio_n_33"))
|
||||
platform.request("user_sma_gpio_n"))
|
||||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=128))
|
||||
|
||||
@ -565,9 +579,29 @@ class Phaser(_NIST_Ions):
|
||||
|
||||
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
|
||||
rtio_channels.append(rtio.LogChannel())
|
||||
self.add_rtio(rtio_channels, _PhaserCRG(platform, self.crg.cd_sys.clk))
|
||||
|
||||
self.comb += self.rtio_crg.refclk.eq(self.ad9154.jesd.cd_jesd.clk)
|
||||
self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels)
|
||||
self.config["RTIO_DDS_COUNT"] = 1
|
||||
self.config["DDS_CHANNELS_PER_BUS"] = 1
|
||||
self.config["DDS_AD9914"] = None
|
||||
self.config["DDS_ONEHOT_SEL"] = None
|
||||
self.config["DDS_RTIO_CLK_RATIO"] = 8
|
||||
|
||||
self.submodules.rtio_crg = _PhaserCRG(
|
||||
platform, self.ad9154.jesd.cd_jesd.clk)
|
||||
self.csr_devices.append("rtio_crg")
|
||||
self.submodules.rtio = rtio.RTIO(rtio_channels)
|
||||
self.register_kernel_cpu_csrdevice("rtio")
|
||||
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
|
||||
self.csr_devices.append("rtio_moninj")
|
||||
self.submodules.rtio_analyzer = rtio.Analyzer(
|
||||
self.rtio, self.get_native_sdram_if())
|
||||
self.csr_devices.append("rtio_analyzer")
|
||||
|
||||
self.config["RTIO_FINE_TS_WIDTH"] = self.rtio.fine_ts_width
|
||||
platform.add_false_path_constraints(
|
||||
self.crg.cd_sys.clk,
|
||||
self.rtio_crg.cd_rtio.clk)
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -206,10 +206,11 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd
|
||||
rtio_channels.append(rtio.Channel.from_phy(
|
||||
phy, ofifo_depth=64, ififo_depth=64))
|
||||
|
||||
self.config["HAS_DDS"] = None
|
||||
self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels)
|
||||
self.config["RTIO_DDS_COUNT"] = 1
|
||||
self.config["DDS_CHANNELS_PER_BUS"] = 8
|
||||
self.config["DDS_AD9858"] = True
|
||||
self.config["DDS_AD9858"] = None
|
||||
dds_pins = platform.request("dds")
|
||||
self.comb += dds_pins.p.eq(0)
|
||||
phy = dds.AD9858(dds_pins, 8)
|
||||
|
@ -7,7 +7,7 @@ from functools import wraps
|
||||
import numpy
|
||||
|
||||
|
||||
__all__ = ["kernel", "portable", "syscall", "host_only",
|
||||
__all__ = ["kernel", "portable", "rpc", "syscall", "host_only",
|
||||
"set_time_manager", "set_watchdog_factory",
|
||||
"TerminationRequested"]
|
||||
|
||||
@ -22,7 +22,7 @@ __all__.extend(kernel_globals)
|
||||
|
||||
|
||||
_ARTIQEmbeddedInfo = namedtuple("_ARTIQEmbeddedInfo",
|
||||
"core_name function syscall forbidden flags")
|
||||
"core_name portable function syscall forbidden flags")
|
||||
|
||||
def kernel(arg=None, flags={}):
|
||||
"""
|
||||
@ -53,7 +53,7 @@ def kernel(arg=None, flags={}):
|
||||
def run_on_core(self, *k_args, **k_kwargs):
|
||||
return getattr(self, arg).run(run_on_core, ((self,) + k_args), k_kwargs)
|
||||
run_on_core.artiq_embedded = _ARTIQEmbeddedInfo(
|
||||
core_name=arg, function=function, syscall=None,
|
||||
core_name=arg, portable=False, function=function, syscall=None,
|
||||
forbidden=False, flags=set(flags))
|
||||
return run_on_core
|
||||
return inner_decorator
|
||||
@ -83,7 +83,23 @@ def portable(arg=None, flags={}):
|
||||
return inner_decorator
|
||||
else:
|
||||
arg.artiq_embedded = \
|
||||
_ARTIQEmbeddedInfo(core_name=None, function=arg, syscall=None,
|
||||
_ARTIQEmbeddedInfo(core_name=None, portable=True, function=arg, syscall=None,
|
||||
forbidden=False, flags=set(flags))
|
||||
return arg
|
||||
|
||||
def rpc(arg=None, flags={}):
|
||||
"""
|
||||
This decorator marks a function for execution on the host interpreter.
|
||||
This is also the default behavior of ARTIQ; however, this decorator allows
|
||||
specifying additional flags.
|
||||
"""
|
||||
if arg is None:
|
||||
def inner_decorator(function):
|
||||
return rpc(function, flags)
|
||||
return inner_decorator
|
||||
else:
|
||||
arg.artiq_embedded = \
|
||||
_ARTIQEmbeddedInfo(core_name=None, portable=False, function=arg, syscall=None,
|
||||
forbidden=False, flags=set(flags))
|
||||
return arg
|
||||
|
||||
@ -101,7 +117,7 @@ def syscall(arg=None, flags={}):
|
||||
if isinstance(arg, str):
|
||||
def inner_decorator(function):
|
||||
function.artiq_embedded = \
|
||||
_ARTIQEmbeddedInfo(core_name=None, function=None,
|
||||
_ARTIQEmbeddedInfo(core_name=None, portable=False, function=None,
|
||||
syscall=function.__name__, forbidden=False,
|
||||
flags=set(flags))
|
||||
return function
|
||||
@ -119,7 +135,7 @@ def host_only(function):
|
||||
in the host Python interpreter.
|
||||
"""
|
||||
function.artiq_embedded = \
|
||||
_ARTIQEmbeddedInfo(core_name=None, function=None, syscall=None,
|
||||
_ARTIQEmbeddedInfo(core_name=None, portable=False, function=None, syscall=None,
|
||||
forbidden=True, flags={})
|
||||
return function
|
||||
|
||||
|
@ -3,6 +3,7 @@ from inspect import isclass
|
||||
|
||||
from artiq.protocols import pyon
|
||||
from artiq.language import units
|
||||
from artiq.language.core import rpc
|
||||
|
||||
|
||||
__all__ = ["NoDefault",
|
||||
@ -274,6 +275,7 @@ class HasEnvironment:
|
||||
kernel_invariants = getattr(self, "kernel_invariants", set())
|
||||
self.kernel_invariants = kernel_invariants | {key}
|
||||
|
||||
@rpc(flags={"async"})
|
||||
def set_dataset(self, key, value,
|
||||
broadcast=False, persist=False, save=True):
|
||||
"""Sets the contents and handling modes of a dataset.
|
||||
@ -290,6 +292,7 @@ class HasEnvironment:
|
||||
"""
|
||||
self.__dataset_mgr.set(key, value, broadcast, persist, save)
|
||||
|
||||
@rpc(flags={"async"})
|
||||
def mutate_dataset(self, key, index, value):
|
||||
"""Mutate an existing dataset at the given index (e.g. set a value at
|
||||
a given position in a NumPy array)
|
||||
@ -303,7 +306,7 @@ class HasEnvironment:
|
||||
as ``slice(*sub_tuple)`` (multi-dimensional slicing)."""
|
||||
self.__dataset_mgr.mutate(key, index, value)
|
||||
|
||||
def get_dataset(self, key, default=NoDefault):
|
||||
def get_dataset(self, key, default=NoDefault, archive=True):
|
||||
"""Returns the contents of a dataset.
|
||||
|
||||
The local storage is searched first, followed by the master storage
|
||||
@ -312,19 +315,25 @@ class HasEnvironment:
|
||||
|
||||
If the dataset does not exist, returns the default value. If no default
|
||||
is provided, raises ``KeyError``.
|
||||
|
||||
By default, datasets obtained by this method are archived into the output
|
||||
HDF5 file of the experiment. If an archived dataset is requested more
|
||||
than one time (and therefore its value has potentially changed) or is
|
||||
modified, a warning is emitted. Archival can be turned off by setting
|
||||
the ``archive`` argument to ``False``.
|
||||
"""
|
||||
try:
|
||||
return self.__dataset_mgr.get(key)
|
||||
return self.__dataset_mgr.get(key, archive)
|
||||
except KeyError:
|
||||
if default is NoDefault:
|
||||
raise
|
||||
else:
|
||||
return default
|
||||
|
||||
def setattr_dataset(self, key, default=NoDefault):
|
||||
def setattr_dataset(self, key, default=NoDefault, archive=True):
|
||||
"""Sets the contents of a dataset as attribute. The names of the
|
||||
dataset and of the attribute are the same."""
|
||||
setattr(self, key, self.get_dataset(key, default))
|
||||
setattr(self, key, self.get_dataset(key, default, archive))
|
||||
|
||||
|
||||
class Experiment:
|
||||
|
@ -6,7 +6,7 @@ annotations.
|
||||
from artiq.compiler import types, builtins
|
||||
|
||||
__all__ = ["TNone", "TBool", "TInt32", "TInt64", "TFloat",
|
||||
"TStr", "TList", "TRange32", "TRange64", "TVar"]
|
||||
"TStr", "TTuple", "TList", "TRange32", "TRange64", "TVar"]
|
||||
|
||||
TNone = builtins.TNone()
|
||||
TBool = builtins.TBool()
|
||||
@ -14,6 +14,7 @@ TInt32 = builtins.TInt(types.TValue(32))
|
||||
TInt64 = builtins.TInt(types.TValue(64))
|
||||
TFloat = builtins.TFloat()
|
||||
TStr = builtins.TStr()
|
||||
TTuple = types.TTuple
|
||||
TList = builtins.TList
|
||||
TRange32 = builtins.TRange(builtins.TInt(types.TValue(32)))
|
||||
TRange64 = builtins.TRange(builtins.TInt(types.TValue(64)))
|
||||
|
@ -18,3 +18,4 @@ _register_unit("s", "pnum_")
|
||||
_register_unit("Hz", "_kMG")
|
||||
_register_unit("dB", "_")
|
||||
_register_unit("V", "um_k")
|
||||
_register_unit("A", "um_")
|
||||
|
@ -402,8 +402,12 @@ class Scheduler:
|
||||
if self._pipelines:
|
||||
logger.warning("some pipelines were not garbage-collected")
|
||||
|
||||
def submit(self, pipeline_name, expid, priority, due_date, flush):
|
||||
"""Submits a new run."""
|
||||
def submit(self, pipeline_name, expid, priority=0, due_date=None, flush=False):
|
||||
"""Submits a new run.
|
||||
|
||||
When called through an experiment, the default values of
|
||||
``pipeline_name``, ``expid`` and ``priority`` correspond to those of
|
||||
the current run."""
|
||||
# mutates expid to insert head repository revision if None
|
||||
if self._terminated:
|
||||
return
|
||||
@ -446,6 +450,9 @@ class Scheduler:
|
||||
whether returning control to the host and pausing would have an effect,
|
||||
in order to avoid the cost of switching kernels in the common case
|
||||
where ``pause`` does nothing.
|
||||
|
||||
This function does not have side effects, and does not have to be
|
||||
followed by a call to ``pause``.
|
||||
"""
|
||||
for pipeline in self._pipelines.values():
|
||||
if rid in pipeline.pool.runs:
|
||||
|
@ -181,23 +181,35 @@ class DatasetManager:
|
||||
def __init__(self, ddb):
|
||||
self.broadcast = Notifier(dict())
|
||||
self.local = dict()
|
||||
self.archive = dict()
|
||||
|
||||
self.ddb = ddb
|
||||
self.broadcast.publish = ddb.update
|
||||
|
||||
def set(self, key, value, broadcast=False, persist=False, save=True):
|
||||
if key in self.archive:
|
||||
logger.warning("Modifying dataset '%s' which is in archive, "
|
||||
"archive will remain untouched",
|
||||
key, stack_info=True)
|
||||
|
||||
if persist:
|
||||
broadcast = True
|
||||
if broadcast:
|
||||
self.broadcast[key] = persist, value
|
||||
elif key in self.broadcast.read:
|
||||
del self.broadcast[key]
|
||||
if save:
|
||||
self.local[key] = value
|
||||
elif key in self.local:
|
||||
del self.local[key]
|
||||
|
||||
def mutate(self, key, index, value):
|
||||
target = None
|
||||
if key in self.local:
|
||||
target = self.local[key]
|
||||
if key in self.broadcast.read:
|
||||
if target is not None:
|
||||
assert target is self.broadcast.read[key][1]
|
||||
target = self.broadcast[key][1]
|
||||
if target is None:
|
||||
raise KeyError("Cannot mutate non-existing dataset")
|
||||
@ -209,12 +221,22 @@ class DatasetManager:
|
||||
index = slice(*index)
|
||||
setitem(target, index, value)
|
||||
|
||||
def get(self, key):
|
||||
def get(self, key, archive=False):
|
||||
if key in self.local:
|
||||
return self.local[key]
|
||||
else:
|
||||
return self.ddb.get(key)
|
||||
data = self.ddb.get(key)
|
||||
if archive:
|
||||
if key in self.archive:
|
||||
logger.warning("Dataset '%s' is already in archive, "
|
||||
"overwriting", key, stack_info=True)
|
||||
self.archive[key] = data
|
||||
return data
|
||||
|
||||
def write_hdf5(self, f):
|
||||
datasets_group = f.create_group("datasets")
|
||||
for k, v in self.local.items():
|
||||
f[k] = v
|
||||
datasets_group[k] = v
|
||||
archive_group = f.create_group("archive")
|
||||
for k, v in self.archive.items():
|
||||
archive_group[k] = v
|
||||
|
@ -79,32 +79,39 @@ set_watchdog_factory(Watchdog)
|
||||
|
||||
|
||||
class Scheduler:
|
||||
pause_noexc = staticmethod(make_parent_action("pause"))
|
||||
|
||||
@host_only
|
||||
def pause(self):
|
||||
if self.pause_noexc():
|
||||
raise TerminationRequested
|
||||
|
||||
submit = staticmethod(make_parent_action("scheduler_submit"))
|
||||
delete = staticmethod(make_parent_action("scheduler_delete"))
|
||||
request_termination = staticmethod(
|
||||
make_parent_action("scheduler_request_termination"))
|
||||
get_status = staticmethod(make_parent_action("scheduler_get_status"))
|
||||
|
||||
def set_run_info(self, rid, pipeline_name, expid, priority):
|
||||
self.rid = rid
|
||||
self.pipeline_name = pipeline_name
|
||||
self.expid = expid
|
||||
self.priority = priority
|
||||
|
||||
_check_pause = staticmethod(make_parent_action("scheduler_check_pause"))
|
||||
pause_noexc = staticmethod(make_parent_action("pause"))
|
||||
@host_only
|
||||
def pause(self):
|
||||
if self.pause_noexc():
|
||||
raise TerminationRequested
|
||||
|
||||
_check_pause = staticmethod(make_parent_action("scheduler_check_pause"))
|
||||
def check_pause(self, rid=None) -> TBool:
|
||||
if rid is None:
|
||||
rid = self.rid
|
||||
return self._check_pause(rid)
|
||||
|
||||
_submit = staticmethod(make_parent_action("scheduler_submit"))
|
||||
def submit(self, pipeline_name=None, expid=None, priority=None, due_date=None, flush=False):
|
||||
if pipeline_name is None:
|
||||
pipeline_name = self.pipeline_name
|
||||
if expid is None:
|
||||
expid = self.expid
|
||||
if priority is None:
|
||||
priority = self.priority
|
||||
return self._submit(pipeline_name, expid, priority, due_date, flush)
|
||||
|
||||
delete = staticmethod(make_parent_action("scheduler_delete"))
|
||||
request_termination = staticmethod(
|
||||
make_parent_action("scheduler_request_termination"))
|
||||
get_status = staticmethod(make_parent_action("scheduler_get_status"))
|
||||
|
||||
|
||||
class CCB:
|
||||
issue = staticmethod(make_parent_action("ccb_issue"))
|
||||
@ -238,7 +245,7 @@ def main():
|
||||
elif action == "write_results":
|
||||
filename = "{:09}-{}.h5".format(rid, exp.__name__)
|
||||
with h5py.File(filename, "w") as f:
|
||||
dataset_mgr.write_hdf5(f.create_group("datasets"))
|
||||
dataset_mgr.write_hdf5(f)
|
||||
f["artiq_version"] = artiq_version
|
||||
f["rid"] = rid
|
||||
f["start_time"] = int(time.mktime(start_time))
|
||||
|
54
artiq/runtime.rs/Cargo.lock
generated
54
artiq/runtime.rs/Cargo.lock
generated
@ -2,15 +2,24 @@
|
||||
name = "runtime"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fringe 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log_buffer 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lwip 0.0.0",
|
||||
"std_artiq 0.0.0",
|
||||
"walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloc_artiq"
|
||||
version = "0.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "fringe"
|
||||
version = "1.1.0"
|
||||
@ -19,11 +28,30 @@ dependencies = [
|
||||
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "log_buffer"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lwip"
|
||||
version = "0.0.0"
|
||||
@ -43,6 +71,32 @@ dependencies = [
|
||||
"alloc_artiq 0.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-build"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
|
||||
"checksum fringe 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "987689dcfad85eee8d76b477865641ec483e63fb86d52966bfc350c4a647d78a"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2"
|
||||
"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054"
|
||||
"checksum log_buffer 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec57723b84bbe7bdf76aa93169c9b59e67473317c6de3a83cb2a0f8ccb2aa493"
|
||||
"checksum walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c66c0b9792f0a765345452775f3adbd28dde9d33f30d13e5dcc5ae17cf6f3780"
|
||||
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||
|
@ -2,16 +2,23 @@
|
||||
authors = ["The ARTIQ Project Developers"]
|
||||
name = "runtime"
|
||||
version = "0.0.0"
|
||||
build = "build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
walkdir = "0.1"
|
||||
|
||||
[lib]
|
||||
name = "artiq_rust"
|
||||
name = "runtime"
|
||||
crate-type = ["staticlib"]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
std_artiq = { path = "libstd_artiq" }
|
||||
std_artiq = { path = "libstd_artiq", features = ["alloc"] }
|
||||
lwip = { path = "liblwip", default-features = false }
|
||||
fringe = { version = "= 1.1.0", default-features = false, features = ["alloc"] }
|
||||
lwip = { path = "liblwip" }
|
||||
log = { version = "0.3", default-features = false, features = ["max_level_debug"] }
|
||||
log_buffer = { version = "1.0" }
|
||||
byteorder = { version = "0.5", default-features = false }
|
||||
|
||||
[profile.dev]
|
||||
panic = 'abort'
|
||||
|
42
artiq/runtime.rs/build.rs
Normal file
42
artiq/runtime.rs/build.rs
Normal file
@ -0,0 +1,42 @@
|
||||
extern crate walkdir;
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
use walkdir::WalkDir;
|
||||
|
||||
fn main() {
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let dest_path = Path::new(&out_dir).join("git_info.rs");
|
||||
let mut f = File::create(&dest_path).unwrap();
|
||||
|
||||
writeln!(f, "const GIT_COMMIT: &'static str = {:?};",
|
||||
git_describe().unwrap()).unwrap();
|
||||
|
||||
println!("cargo:rerun-if-changed=../../.git/HEAD");
|
||||
for entry in WalkDir::new("../../.git/refs") {
|
||||
let entry = entry.unwrap();
|
||||
println!("cargo:rerun-if-changed={}", entry.path().display());
|
||||
}
|
||||
}
|
||||
|
||||
// Returns `None` if git is not available.
|
||||
fn git_describe() -> Option<String> {
|
||||
Command::new("git")
|
||||
.arg("describe")
|
||||
.arg("--tags")
|
||||
.arg("--dirty")
|
||||
.arg("--always")
|
||||
.arg("--long")
|
||||
.output()
|
||||
.ok()
|
||||
.and_then(|o| String::from_utf8(o.stdout).ok())
|
||||
.map(|mut s| {
|
||||
let len = s.trim_right().len();
|
||||
s.truncate(len);
|
||||
s
|
||||
})
|
||||
}
|
26
artiq/runtime.rs/libksupport/Cargo.lock
generated
Normal file
26
artiq/runtime.rs/libksupport/Cargo.lock
generated
Normal file
@ -0,0 +1,26 @@
|
||||
[root]
|
||||
name = "ksupport"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"std_artiq 0.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloc_artiq"
|
||||
version = "0.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "std_artiq"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"alloc_artiq 0.0.0",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
|
17
artiq/runtime.rs/libksupport/Cargo.toml
Normal file
17
artiq/runtime.rs/libksupport/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
authors = ["The ARTIQ Project Developers"]
|
||||
name = "ksupport"
|
||||
version = "0.0.0"
|
||||
|
||||
[lib]
|
||||
name = "ksupport"
|
||||
path = "lib.rs"
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
std_artiq = { path = "../libstd_artiq" }
|
||||
byteorder = { version = "0.5", default-features = false }
|
||||
|
||||
[profile.dev]
|
||||
panic = 'unwind'
|
||||
opt-level = 2
|
138
artiq/runtime.rs/libksupport/api.rs
Normal file
138
artiq/runtime.rs/libksupport/api.rs
Normal file
@ -0,0 +1,138 @@
|
||||
use libc::{c_void, c_char, size_t};
|
||||
|
||||
macro_rules! api {
|
||||
($i:ident) => ({
|
||||
extern { static $i: c_void; }
|
||||
api!($i = &$i as *const _)
|
||||
});
|
||||
($i:ident, $d:item) => ({
|
||||
$d
|
||||
api!($i = $i)
|
||||
});
|
||||
($i:ident = $e:expr) => {
|
||||
(stringify!($i), unsafe { $e as *const () })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve(required: &str) -> usize {
|
||||
unsafe {
|
||||
API.iter()
|
||||
.find(|&&(exported, _)| exported == required)
|
||||
.map(|&(_, ptr)| ptr as usize)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
static mut API: &'static [(&'static str, *const ())] = &[
|
||||
api!(__divsi3),
|
||||
api!(__modsi3),
|
||||
api!(__ledf2),
|
||||
api!(__gedf2),
|
||||
api!(__unorddf2),
|
||||
api!(__eqdf2),
|
||||
api!(__ltdf2),
|
||||
api!(__nedf2),
|
||||
api!(__gtdf2),
|
||||
api!(__negsf2),
|
||||
api!(__negdf2),
|
||||
api!(__addsf3),
|
||||
api!(__subsf3),
|
||||
api!(__mulsf3),
|
||||
api!(__divsf3),
|
||||
api!(__lshrdi3),
|
||||
api!(__muldi3),
|
||||
api!(__divdi3),
|
||||
api!(__ashldi3),
|
||||
api!(__ashrdi3),
|
||||
api!(__udivmoddi4),
|
||||
api!(__floatsisf),
|
||||
api!(__floatunsisf),
|
||||
api!(__fixsfsi),
|
||||
api!(__fixunssfsi),
|
||||
api!(__adddf3),
|
||||
api!(__subdf3),
|
||||
api!(__muldf3),
|
||||
api!(__divdf3),
|
||||
api!(__floatsidf),
|
||||
api!(__floatunsidf),
|
||||
api!(__floatdidf),
|
||||
api!(__fixdfsi),
|
||||
api!(__fixdfdi),
|
||||
api!(__fixunsdfsi),
|
||||
api!(__clzsi2),
|
||||
api!(__ctzsi2),
|
||||
api!(__udivdi3),
|
||||
api!(__umoddi3),
|
||||
api!(__moddi3),
|
||||
api!(__powidf2),
|
||||
|
||||
/* libc */
|
||||
api!(strcmp),
|
||||
api!(strlen, extern { fn strlen(s: *const c_char) -> size_t; }),
|
||||
api!(abort = ::abort),
|
||||
|
||||
/* libm */
|
||||
api!(sqrt),
|
||||
api!(round),
|
||||
|
||||
/* exceptions */
|
||||
api!(_Unwind_Resume),
|
||||
api!(__artiq_personality),
|
||||
api!(__artiq_raise),
|
||||
api!(__artiq_reraise),
|
||||
|
||||
/* proxified syscalls */
|
||||
api!(core_log),
|
||||
|
||||
api!(now = &::NOW as *const _),
|
||||
|
||||
api!(watchdog_set = ::watchdog_set),
|
||||
api!(watchdog_clear = ::watchdog_clear),
|
||||
|
||||
api!(send_rpc = ::send_rpc),
|
||||
api!(send_async_rpc = ::send_async_rpc),
|
||||
api!(recv_rpc = ::recv_rpc),
|
||||
|
||||
api!(cache_get = ::cache_get),
|
||||
api!(cache_put = ::cache_put),
|
||||
|
||||
/* direct syscalls */
|
||||
api!(rtio_init),
|
||||
api!(rtio_get_counter),
|
||||
api!(rtio_log),
|
||||
api!(rtio_output),
|
||||
api!(rtio_input_timestamp),
|
||||
api!(rtio_input_data),
|
||||
|
||||
#[cfg(has_dds)]
|
||||
api!(dds_init),
|
||||
#[cfg(has_dds)]
|
||||
api!(dds_init_sync),
|
||||
#[cfg(has_dds)]
|
||||
api!(dds_batch_enter),
|
||||
#[cfg(has_dds)]
|
||||
api!(dds_batch_exit),
|
||||
#[cfg(has_dds)]
|
||||
api!(dds_set),
|
||||
|
||||
api!(i2c_init),
|
||||
api!(i2c_start),
|
||||
api!(i2c_stop),
|
||||
api!(i2c_write),
|
||||
api!(i2c_read),
|
||||
|
||||
// #if (defined CONFIG_AD9154_CS)
|
||||
api!(ad9154_init),
|
||||
api!(ad9154_write),
|
||||
api!(ad9154_read),
|
||||
|
||||
api!(ad9516_write),
|
||||
api!(ad9516_read),
|
||||
|
||||
api!(ad9154_jesd_enable),
|
||||
api!(ad9154_jesd_ready),
|
||||
api!(ad9154_jesd_prbs),
|
||||
api!(ad9154_jesd_stpl),
|
||||
// #endif
|
||||
];
|
56
artiq/runtime.rs/libksupport/dyld.rs
Normal file
56
artiq/runtime.rs/libksupport/dyld.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use core::{ptr, slice, str};
|
||||
use libc::{c_void, c_char, c_int, size_t};
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
#[derive(Default)]
|
||||
struct dyld_info {
|
||||
__opaque: [usize; 7]
|
||||
}
|
||||
|
||||
extern {
|
||||
fn dyld_load(shlib: *const c_void, base: usize,
|
||||
resolve: extern fn(*mut c_void, *const c_char) -> usize,
|
||||
resolve_data: *mut c_void,
|
||||
info: *mut dyld_info, error_out: *mut *const c_char) -> c_int;
|
||||
fn dyld_lookup(symbol: *const c_char, info: *const dyld_info) -> *const c_void;
|
||||
|
||||
fn strlen(ptr: *const c_char) -> size_t;
|
||||
}
|
||||
|
||||
pub struct Library {
|
||||
lower: dyld_info
|
||||
}
|
||||
|
||||
impl Library {
|
||||
pub unsafe fn load<F>(shlib: &[u8], base: usize, mut resolve: F)
|
||||
-> Result<Library, &'static str>
|
||||
where F: Fn(&str) -> usize {
|
||||
extern fn wrapper<F>(data: *mut c_void, name: *const c_char) -> usize
|
||||
where F: Fn(&str) -> usize {
|
||||
unsafe {
|
||||
let f = data as *mut F;
|
||||
let name = slice::from_raw_parts(name as *const u8, strlen(name));
|
||||
(*f)(str::from_utf8_unchecked(name))
|
||||
}
|
||||
}
|
||||
|
||||
let mut library = Library { lower: dyld_info::default() };
|
||||
let mut error: *const c_char = ptr::null();
|
||||
if dyld_load(shlib.as_ptr() as *const _, base,
|
||||
wrapper::<F>, &mut resolve as *mut _ as *mut _,
|
||||
&mut library.lower, &mut error) == 0 {
|
||||
let error = slice::from_raw_parts(error as *const u8, strlen(error));
|
||||
Err(str::from_utf8_unchecked(error))
|
||||
} else {
|
||||
Ok(library)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn lookup(&self, symbol: &str) -> usize {
|
||||
assert!(symbol.len() < 32);
|
||||
let mut buf = [0u8; 32];
|
||||
buf[0..symbol.as_bytes().len()].copy_from_slice(symbol.as_bytes());
|
||||
dyld_lookup(&buf as *const _ as *const c_char, &self.lower) as usize
|
||||
}
|
||||
}
|
308
artiq/runtime.rs/libksupport/lib.rs
Normal file
308
artiq/runtime.rs/libksupport/lib.rs
Normal file
@ -0,0 +1,308 @@
|
||||
#![feature(lang_items, needs_panic_runtime, asm, libc, stmt_expr_attributes)]
|
||||
|
||||
#![no_std]
|
||||
#![needs_panic_runtime]
|
||||
|
||||
#[macro_use]
|
||||
extern crate std_artiq as std;
|
||||
extern crate libc;
|
||||
extern crate byteorder;
|
||||
|
||||
#[path = "../src/board.rs"]
|
||||
mod board;
|
||||
#[path = "../src/mailbox.rs"]
|
||||
mod mailbox;
|
||||
|
||||
#[path = "../src/proto.rs"]
|
||||
mod proto;
|
||||
#[path = "../src/kernel_proto.rs"]
|
||||
mod kernel_proto;
|
||||
#[path = "../src/rpc_proto.rs"]
|
||||
mod rpc_proto;
|
||||
|
||||
mod dyld;
|
||||
mod api;
|
||||
|
||||
use core::{mem, ptr, slice, str};
|
||||
use std::io::Cursor;
|
||||
use libc::{c_char, size_t};
|
||||
use kernel_proto::*;
|
||||
use dyld::Library;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn malloc(_size: usize) -> *mut libc::c_void {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn realloc(_ptr: *mut libc::c_void, _size: usize) -> *mut libc::c_void {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn free(_ptr: *mut libc::c_void) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn send(request: &Message) {
|
||||
unsafe { mailbox::send(request as *const _ as usize) }
|
||||
while !mailbox::acknowledged() {}
|
||||
}
|
||||
|
||||
fn recv<R, F: FnOnce(&Message) -> R>(f: F) -> R {
|
||||
while mailbox::receive() == 0 {}
|
||||
let result = f(unsafe { mem::transmute::<usize, &Message>(mailbox::receive()) });
|
||||
mailbox::acknowledge();
|
||||
result
|
||||
}
|
||||
|
||||
macro_rules! recv {
|
||||
($p:pat => $e:expr) => {
|
||||
recv(|request| {
|
||||
if let $p = request {
|
||||
$e
|
||||
} else {
|
||||
send(&Log(format_args!("unexpected reply: {:?}", request)));
|
||||
loop {}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => ($crate::send(&$crate::kernel_proto::Log(format_args!($($arg)*))));
|
||||
}
|
||||
|
||||
macro_rules! println {
|
||||
($fmt:expr) => (print!(concat!($fmt, "\n")));
|
||||
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
|
||||
}
|
||||
|
||||
#[path = "../src/rpc_queue.rs"]
|
||||
mod rpc_queue;
|
||||
|
||||
#[lang = "panic_fmt"]
|
||||
extern fn panic_fmt(args: core::fmt::Arguments, file: &'static str, line: u32) -> ! {
|
||||
println!("panic at {}:{}: {}", file, line, args);
|
||||
send(&RunAborted);
|
||||
loop {}
|
||||
}
|
||||
|
||||
static mut NOW: u64 = 0;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn send_to_log(ptr: *const u8, len: usize) {
|
||||
send(&LogSlice(unsafe {
|
||||
str::from_utf8_unchecked(slice::from_raw_parts(ptr, len))
|
||||
}))
|
||||
}
|
||||
|
||||
extern fn abort() -> ! {
|
||||
println!("kernel called abort()");
|
||||
send(&RunAborted);
|
||||
loop {}
|
||||
}
|
||||
|
||||
extern fn send_rpc(service: u32, tag: *const u8, data: *const *const ()) {
|
||||
extern { fn strlen(s: *const c_char) -> size_t; }
|
||||
let tag = unsafe { slice::from_raw_parts(tag, strlen(tag as *const c_char)) };
|
||||
|
||||
while !rpc_queue::empty() {}
|
||||
send(&RpcSend {
|
||||
async: false,
|
||||
service: service,
|
||||
tag: tag,
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
extern fn send_async_rpc(service: u32, tag: *const u8, data: *const *const ()) {
|
||||
extern { fn strlen(s: *const c_char) -> size_t; }
|
||||
let tag = unsafe { slice::from_raw_parts(tag, strlen(tag as *const c_char)) };
|
||||
|
||||
while rpc_queue::full() {}
|
||||
rpc_queue::enqueue(|mut slice| {
|
||||
let length = {
|
||||
let mut writer = Cursor::new(&mut slice[4..]);
|
||||
try!(rpc_proto::send_args(&mut writer, service, tag, data));
|
||||
writer.position()
|
||||
};
|
||||
proto::write_u32(&mut slice, length as u32)
|
||||
}).unwrap_or_else(|err| {
|
||||
assert!(err.kind() == std::io::ErrorKind::WriteZero);
|
||||
|
||||
while !rpc_queue::empty() {}
|
||||
send(&RpcSend {
|
||||
async: true,
|
||||
service: service,
|
||||
tag: tag,
|
||||
data: data
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
extern fn recv_rpc(slot: *mut ()) -> usize {
|
||||
send(&RpcRecvRequest(slot));
|
||||
recv!(&RpcRecvReply(ref result) => {
|
||||
match result {
|
||||
&Ok(alloc_size) => alloc_size,
|
||||
&Err(ref exception) => unsafe { __artiq_raise(exception as *const _) }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(improper_ctypes)]
|
||||
extern {
|
||||
fn __artiq_raise(exn: *const ::kernel_proto::Exception) -> !;
|
||||
}
|
||||
|
||||
macro_rules! artiq_raise {
|
||||
($name:expr, $message:expr) => ({
|
||||
let exn = Exception {
|
||||
name: concat!("0:artiq.coredevice.exceptions.", $name, "\0").as_bytes().as_ptr(),
|
||||
file: concat!(file!(), "\0").as_bytes().as_ptr(),
|
||||
line: line!(),
|
||||
column: column!(),
|
||||
// https://github.com/rust-lang/rfcs/pull/1719
|
||||
function: "(Rust function)\0".as_bytes().as_ptr(),
|
||||
message: concat!($message, "\0").as_bytes().as_ptr(),
|
||||
param: [0; 3],
|
||||
phantom: ::core::marker::PhantomData
|
||||
};
|
||||
unsafe { __artiq_raise(&exn as *const _) }
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn __artiq_terminate(exception: *const kernel_proto::Exception,
|
||||
backtrace_data: *mut usize,
|
||||
backtrace_size: usize) -> ! {
|
||||
let backtrace = unsafe { slice::from_raw_parts_mut(backtrace_data, backtrace_size) };
|
||||
let mut cursor = 0;
|
||||
for index in 0..backtrace.len() {
|
||||
if backtrace[index] > kernel_proto::KERNELCPU_PAYLOAD_ADDRESS {
|
||||
backtrace[cursor] = backtrace[index] - kernel_proto::KERNELCPU_PAYLOAD_ADDRESS;
|
||||
cursor += 1;
|
||||
}
|
||||
}
|
||||
let backtrace = &mut backtrace[0..cursor];
|
||||
|
||||
send(&NowSave(unsafe { NOW }));
|
||||
send(&RunException {
|
||||
exception: unsafe { (*exception).clone() },
|
||||
backtrace: backtrace
|
||||
});
|
||||
loop {}
|
||||
}
|
||||
|
||||
extern fn watchdog_set(ms: i64) -> usize {
|
||||
if ms < 0 {
|
||||
artiq_raise!("ValueError", "cannot set a watchdog with a negative timeout")
|
||||
}
|
||||
|
||||
send(&WatchdogSetRequest { ms: ms as u64 });
|
||||
recv!(&WatchdogSetReply { id } => id)
|
||||
}
|
||||
|
||||
extern fn watchdog_clear(id: usize) {
|
||||
send(&WatchdogClear { id: id })
|
||||
}
|
||||
|
||||
extern fn cache_get(key: *const u8) -> (usize, *const u32) {
|
||||
extern { fn strlen(s: *const c_char) -> size_t; }
|
||||
let key = unsafe { slice::from_raw_parts(key, strlen(key as *const c_char)) };
|
||||
let key = unsafe { str::from_utf8_unchecked(key) };
|
||||
|
||||
send(&CacheGetRequest { key: key });
|
||||
recv!(&CacheGetReply { value } => (value.len(), value.as_ptr()))
|
||||
}
|
||||
|
||||
extern fn cache_put(key: *const u8, &(len, ptr): &(usize, *const u32)) {
|
||||
extern { fn strlen(s: *const c_char) -> size_t; }
|
||||
let key = unsafe { slice::from_raw_parts(key, strlen(key as *const c_char)) };
|
||||
let key = unsafe { str::from_utf8_unchecked(key) };
|
||||
|
||||
let value = unsafe { slice::from_raw_parts(ptr, len) };
|
||||
send(&CachePutRequest { key: key, value: value });
|
||||
recv!(&CachePutReply { succeeded } => {
|
||||
if !succeeded {
|
||||
artiq_raise!("CacheError", "cannot put into a busy cache row")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn attribute_writeback(typeinfo: *const ()) {
|
||||
struct Attr {
|
||||
offset: usize,
|
||||
tag: *const u8,
|
||||
name: *const u8
|
||||
}
|
||||
|
||||
struct Type {
|
||||
attributes: *const *const Attr,
|
||||
objects: *const *const ()
|
||||
}
|
||||
|
||||
let mut tys = typeinfo as *const *const Type;
|
||||
while !(*tys).is_null() {
|
||||
let ty = *tys;
|
||||
tys = tys.offset(1);
|
||||
|
||||
let mut objects = (*ty).objects;
|
||||
while !(*objects).is_null() {
|
||||
let object = *objects;
|
||||
objects = objects.offset(1);
|
||||
|
||||
let mut attributes = (*ty).attributes;
|
||||
while !(*attributes).is_null() {
|
||||
let attribute = *attributes;
|
||||
attributes = attributes.offset(1);
|
||||
|
||||
if !(*attribute).tag.is_null() {
|
||||
send_async_rpc(0, (*attribute).tag, [
|
||||
&object as *const _ as *const (),
|
||||
&(*attribute).name as *const _ as *const (),
|
||||
(object as usize + (*attribute).offset) as *const ()
|
||||
].as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn main() {
|
||||
let library = recv!(&LoadRequest(library) => {
|
||||
match Library::load(library, kernel_proto::KERNELCPU_PAYLOAD_ADDRESS, api::resolve) {
|
||||
Err(error) => {
|
||||
send(&LoadReply(Err(error)));
|
||||
loop {}
|
||||
},
|
||||
Ok(library) => {
|
||||
send(&LoadReply(Ok(())));
|
||||
library
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let __bss_start = library.lookup("__bss_start");
|
||||
let _end = library.lookup("_end");
|
||||
ptr::write_bytes(__bss_start as *mut u8, 0, _end - __bss_start);
|
||||
|
||||
send(&NowInitRequest);
|
||||
recv!(&NowInitReply(now) => NOW = now);
|
||||
(mem::transmute::<usize, fn()>(library.lookup("__modinit__")))();
|
||||
send(&NowSave(NOW));
|
||||
|
||||
attribute_writeback(library.lookup("typeinfo") as *const ());
|
||||
|
||||
send(&RunFinished);
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn exception_handler(vect: u32, _regs: *const u32, pc: u32, ea: u32) {
|
||||
println!("exception {:?} at PC 0x{:x}, EA 0x{:x}", vect, pc, ea);
|
||||
send(&RunAborted)
|
||||
}
|
@ -103,6 +103,10 @@ pub struct udp_pcb {
|
||||
pub const TCP_WRITE_FLAG_COPY: u8 = 0x01;
|
||||
pub const TCP_WRITE_FLAG_MORE: u8 = 0x02;
|
||||
|
||||
pub const SOF_REUSEADDR: u8 = 0x04;
|
||||
pub const SOF_KEEPALIVE: u8 = 0x08;
|
||||
pub const SOF_BROADCAST: u8 = 0x20;
|
||||
|
||||
extern {
|
||||
pub fn pbuf_alloc(l: pbuf_layer, length: u16, type_: pbuf_type) -> *mut pbuf;
|
||||
pub fn pbuf_realloc(p: *mut pbuf, length: u16);
|
||||
@ -122,28 +126,30 @@ extern {
|
||||
pub fn tcp_bind(pcb: *mut tcp_pcb, ipaddr: *mut ip_addr, port: u16) -> err;
|
||||
pub fn tcp_listen_with_backlog(pcb: *mut tcp_pcb, backlog: u8) -> *mut tcp_pcb;
|
||||
pub fn tcp_accept(pcb: *mut tcp_pcb,
|
||||
accept: extern fn(arg: *mut c_void, newpcb: *mut tcp_pcb,
|
||||
err: err) -> err);
|
||||
accept: Option<extern fn(arg: *mut c_void, newpcb: *mut tcp_pcb,
|
||||
err: err) -> err>);
|
||||
pub fn tcp_connect(pcb: *mut tcp_pcb, ipaddr: *mut ip_addr, port: u16,
|
||||
connected: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, err: err)) -> err;
|
||||
pub fn tcp_write(pcb: *mut tcp_pcb, dataptr: *const c_void, len: u16, apiflags: u8) -> err;
|
||||
pub fn tcp_sent(pcb: *mut tcp_pcb,
|
||||
sent: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, len: u16) -> err);
|
||||
sent: Option<extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, len: u16) -> err>);
|
||||
pub fn tcp_recv(pcb: *mut tcp_pcb,
|
||||
recv: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, p: *mut pbuf,
|
||||
err: err) -> err);
|
||||
recv: Option<extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, p: *mut pbuf,
|
||||
err: err) -> err>);
|
||||
pub fn tcp_recved(pcb: *mut tcp_pcb, len: u16);
|
||||
pub fn tcp_poll(pcb: *mut tcp_pcb,
|
||||
poll: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb),
|
||||
poll: Option<extern fn(arg: *mut c_void, tcb: *mut tcp_pcb)>,
|
||||
interval: u8);
|
||||
pub fn tcp_shutdown(pcb: *mut tcp_pcb, shut_rx: c_int, shut_tx: c_int) -> err;
|
||||
pub fn tcp_close(pcb: *mut tcp_pcb) -> err;
|
||||
pub fn tcp_abort(pcb: *mut tcp_pcb);
|
||||
pub fn tcp_err(pcb: *mut tcp_pcb,
|
||||
err: extern fn(arg: *mut c_void, err: err));
|
||||
err: Option<extern fn(arg: *mut c_void, err: err)>);
|
||||
|
||||
// nonstandard
|
||||
pub fn tcp_sndbuf_(pcb: *mut tcp_pcb) -> u16;
|
||||
pub fn tcp_so_options_(pcb: *mut tcp_pcb) -> *mut u8;
|
||||
pub fn tcp_nagle_disable_(pcb: *mut tcp_pcb);
|
||||
|
||||
pub fn udp_new() -> *mut udp_pcb;
|
||||
pub fn udp_new_ip_type(type_: ip_addr_type) -> *mut udp_pcb;
|
||||
@ -154,7 +160,7 @@ extern {
|
||||
pub fn udp_send(pcb: *mut udp_pcb, p: *mut pbuf) -> err;
|
||||
pub fn udp_sendto(pcb: *mut udp_pcb, p: *mut pbuf, ipaddr: *mut ip_addr, port: u16) -> err;
|
||||
pub fn udp_recv(pcb: *mut udp_pcb,
|
||||
recv: extern fn(arg: *mut c_void, upcb: *mut udp_pcb, p: *mut pbuf,
|
||||
addr: *mut ip_addr, port: u16),
|
||||
recv: Option<extern fn(arg: *mut c_void, upcb: *mut udp_pcb, p: *mut pbuf,
|
||||
addr: *mut ip_addr, port: u16)>,
|
||||
recv_arg: *mut c_void);
|
||||
}
|
||||
|
@ -9,4 +9,8 @@ path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
lwip-sys = { path = "../liblwip-sys" }
|
||||
std_artiq = { path = "../libstd_artiq" }
|
||||
std_artiq = { path = "../libstd_artiq", features = ["alloc"] }
|
||||
|
||||
[features]
|
||||
default = ["preemption"]
|
||||
preemption = []
|
||||
|
@ -8,7 +8,9 @@ extern crate lwip_sys;
|
||||
extern crate std_artiq as std;
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use core::ptr;
|
||||
use core::cell::RefCell;
|
||||
use core::fmt;
|
||||
use alloc::boxed::Box;
|
||||
use collections::LinkedList;
|
||||
use libc::c_void;
|
||||
@ -32,6 +34,8 @@ pub enum Error {
|
||||
ConnectionReset,
|
||||
ConnectionClosed,
|
||||
IllegalArgument,
|
||||
// Not used by lwip; added for building blocking interfaces.
|
||||
Interrupted
|
||||
}
|
||||
|
||||
impl Error {
|
||||
@ -53,6 +57,7 @@ impl Error {
|
||||
Error::ConnectionReset => "connection reset",
|
||||
Error::ConnectionClosed => "connection closed",
|
||||
Error::IllegalArgument => "illegal argument",
|
||||
Error::Interrupted => "interrupted"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -71,7 +76,12 @@ impl error::Error for Error {
|
||||
|
||||
impl From<Error> for std::io::Error {
|
||||
fn from(lower: Error) -> std::io::Error {
|
||||
std::io::Error::new(std::io::ErrorKind::Other, lower)
|
||||
use std::io;
|
||||
|
||||
match lower {
|
||||
Error::Interrupted => io::Error::new(io::ErrorKind::Interrupted, "interrupted"),
|
||||
err => io::Error::new(io::ErrorKind::Other, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,19 +112,54 @@ fn result_from<T, F>(err: lwip_sys::err, f: F) -> Result<T>
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum IpAddr {
|
||||
Ip4([u8; 4]),
|
||||
Ip6([u16; 8]),
|
||||
IpAny
|
||||
V4([u8; 4]),
|
||||
V6([u16; 8]),
|
||||
Any
|
||||
}
|
||||
|
||||
pub const IP4_ANY: IpAddr = IpAddr::Ip4([0, 0, 0, 0]);
|
||||
pub const IP6_ANY: IpAddr = IpAddr::Ip6([0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
pub const IP_ANY: IpAddr = IpAddr::IpAny;
|
||||
pub const IP4_ANY: IpAddr = IpAddr::V4([0, 0, 0, 0]);
|
||||
pub const IP6_ANY: IpAddr = IpAddr::V6([0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
pub const IP_ANY: IpAddr = IpAddr::Any;
|
||||
|
||||
impl fmt::Display for IpAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
IpAddr::V4(ref octets) =>
|
||||
write!(f, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]),
|
||||
|
||||
IpAddr::V6(ref segments) => {
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum State { Head, Skip, Tail };
|
||||
|
||||
let mut state = State::Head;
|
||||
for (idx, &segment) in segments.iter().enumerate() {
|
||||
match state {
|
||||
State::Head | State::Skip if segment == 0 =>
|
||||
state = State::Skip,
|
||||
State::Skip if segment != 0 => {
|
||||
state = State::Tail;
|
||||
try!(write!(f, ":{:x}", segment))
|
||||
}
|
||||
_ => try!(write!(f, "{:x}", segment))
|
||||
}
|
||||
|
||||
if state != State::Skip && idx != 15 {
|
||||
try!(write!(f, ":"))
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
|
||||
IpAddr::Any =>
|
||||
write!(f, "*")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IpAddr {
|
||||
fn into_raw(self) -> lwip_sys::ip_addr {
|
||||
match self {
|
||||
IpAddr::Ip4(ref octets) =>
|
||||
IpAddr::V4(octets) =>
|
||||
lwip_sys::ip_addr {
|
||||
data: [(octets[0] as u32) << 24 |
|
||||
(octets[1] as u32) << 16 |
|
||||
@ -123,7 +168,7 @@ impl IpAddr {
|
||||
0, 0, 0],
|
||||
type_: lwip_sys::IPADDR_TYPE_V4
|
||||
},
|
||||
IpAddr::Ip6(ref segments) =>
|
||||
IpAddr::V6(segments) =>
|
||||
lwip_sys::ip_addr {
|
||||
data: [(segments[0] as u32) << 16 | (segments[1] as u32),
|
||||
(segments[2] as u32) << 16 | (segments[3] as u32),
|
||||
@ -131,7 +176,7 @@ impl IpAddr {
|
||||
(segments[6] as u32) << 16 | (segments[7] as u32)],
|
||||
type_: lwip_sys::IPADDR_TYPE_V6
|
||||
},
|
||||
IpAddr::IpAny =>
|
||||
IpAddr::Any =>
|
||||
lwip_sys::ip_addr {
|
||||
data: [0; 4],
|
||||
type_: lwip_sys::IPADDR_TYPE_ANY
|
||||
@ -142,17 +187,17 @@ impl IpAddr {
|
||||
unsafe fn from_raw(raw: *mut lwip_sys::ip_addr) -> IpAddr {
|
||||
match *raw {
|
||||
lwip_sys::ip_addr { type_: lwip_sys::IPADDR_TYPE_V4, data } =>
|
||||
IpAddr::Ip4([(data[0] >> 24) as u8,
|
||||
IpAddr::V4([(data[0] >> 24) as u8,
|
||||
(data[0] >> 16) as u8,
|
||||
(data[0] >> 8) as u8,
|
||||
(data[0] >> 0) as u8]),
|
||||
lwip_sys::ip_addr { type_: lwip_sys::IPADDR_TYPE_V6, data } =>
|
||||
IpAddr::Ip6([(data[0] >> 16) as u16, data[0] as u16,
|
||||
IpAddr::V6([(data[0] >> 16) as u16, data[0] as u16,
|
||||
(data[1] >> 16) as u16, data[1] as u16,
|
||||
(data[2] >> 16) as u16, data[2] as u16,
|
||||
(data[3] >> 16) as u16, data[3] as u16]),
|
||||
lwip_sys::ip_addr { type_: lwip_sys::IPADDR_TYPE_ANY, .. } =>
|
||||
IpAddr::IpAny
|
||||
IpAddr::Any
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -163,6 +208,12 @@ pub struct SocketAddr {
|
||||
pub port: u16
|
||||
}
|
||||
|
||||
impl fmt::Display for SocketAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}:{}", self.ip, self.port)
|
||||
}
|
||||
}
|
||||
|
||||
impl SocketAddr {
|
||||
pub fn new(ip: IpAddr, port: u16) -> SocketAddr {
|
||||
SocketAddr { ip: ip, port: port }
|
||||
@ -175,6 +226,9 @@ pub struct Pbuf<'payload> {
|
||||
phantom: PhantomData<&'payload [u8]>
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "preemption"))]
|
||||
unsafe impl<'payload> Send for Pbuf<'payload> {}
|
||||
|
||||
impl<'payload> Pbuf<'payload> {
|
||||
unsafe fn from_raw(raw: *mut lwip_sys::pbuf) -> Pbuf<'payload> {
|
||||
Pbuf { raw: raw, phantom: PhantomData }
|
||||
@ -259,6 +313,9 @@ pub struct UdpSocket {
|
||||
state: Box<RefCell<UdpSocketState>>
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "preemption"))]
|
||||
unsafe impl Send for UdpSocket {}
|
||||
|
||||
impl UdpSocket {
|
||||
pub fn new() -> Result<UdpSocket> {
|
||||
extern fn recv(arg: *mut c_void, _pcb: *mut lwip_sys::udp_pcb,
|
||||
@ -279,12 +336,12 @@ impl UdpSocket {
|
||||
recv_buffer: LinkedList::new()
|
||||
}));
|
||||
let arg = &mut *state as *mut RefCell<UdpSocketState> as *mut _;
|
||||
lwip_sys::udp_recv(raw, recv, arg);
|
||||
lwip_sys::udp_recv(raw, Some(recv), arg);
|
||||
Ok(UdpSocket { raw: raw, state: state })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn state(&self) -> *const RefCell<UdpSocketState> {
|
||||
pub fn state(&self) -> &RefCell<UdpSocketState> {
|
||||
&*self.state
|
||||
}
|
||||
|
||||
@ -347,6 +404,9 @@ pub struct TcpListener {
|
||||
state: Box<RefCell<TcpListenerState>>
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "preemption"))]
|
||||
unsafe impl Send for TcpListener {}
|
||||
|
||||
impl TcpListener {
|
||||
pub fn bind(addr: SocketAddr) -> Result<TcpListener> {
|
||||
extern fn accept(arg: *mut c_void, newpcb: *mut lwip_sys::tcp_pcb,
|
||||
@ -376,18 +436,30 @@ impl TcpListener {
|
||||
}));
|
||||
let arg = &mut *state as *mut RefCell<TcpListenerState> as *mut _;
|
||||
lwip_sys::tcp_arg(raw2, arg);
|
||||
lwip_sys::tcp_accept(raw2, accept);
|
||||
lwip_sys::tcp_accept(raw2, Some(accept));
|
||||
Ok(TcpListener { raw: raw2, state: state })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn state(&self) -> *const RefCell<TcpListenerState> {
|
||||
pub fn state(&self) -> &RefCell<TcpListenerState> {
|
||||
&*self.state
|
||||
}
|
||||
|
||||
pub fn try_accept(&self) -> Option<TcpStream> {
|
||||
self.state.borrow_mut().backlog.pop_front()
|
||||
}
|
||||
|
||||
pub fn keepalive(&self) -> bool {
|
||||
unsafe { *lwip_sys::tcp_so_options_(self.raw) & lwip_sys::SOF_KEEPALIVE != 0 }
|
||||
}
|
||||
|
||||
pub fn set_keepalive(&self, keepalive: bool) {
|
||||
if keepalive {
|
||||
unsafe { *lwip_sys::tcp_so_options_(self.raw) |= lwip_sys::SOF_KEEPALIVE }
|
||||
} else {
|
||||
unsafe { *lwip_sys::tcp_so_options_(self.raw) &= !lwip_sys::SOF_KEEPALIVE }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TcpListener {
|
||||
@ -409,7 +481,8 @@ pub enum Shutdown {
|
||||
#[derive(Debug)]
|
||||
pub struct TcpStreamState {
|
||||
recv_buffer: LinkedList<Result<Pbuf<'static>>>,
|
||||
send_avail: usize
|
||||
send_avail: usize,
|
||||
total_sent: usize
|
||||
}
|
||||
|
||||
impl TcpStreamState {
|
||||
@ -428,6 +501,9 @@ pub struct TcpStream {
|
||||
state: Box<RefCell<TcpStreamState>>
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "preemption"))]
|
||||
unsafe impl Send for TcpStream {}
|
||||
|
||||
impl TcpStream {
|
||||
fn from_raw(raw: *mut lwip_sys::tcp_pcb) -> TcpStream {
|
||||
extern fn recv(arg: *mut c_void, _raw: *mut lwip_sys::tcp_pcb,
|
||||
@ -445,10 +521,12 @@ impl TcpStream {
|
||||
}
|
||||
|
||||
extern fn sent(arg: *mut c_void, raw: *mut lwip_sys::tcp_pcb,
|
||||
_len: u16) -> lwip_sys::err {
|
||||
len: u16) -> lwip_sys::err {
|
||||
unsafe {
|
||||
let state = arg as *mut RefCell<TcpStreamState>;
|
||||
(*state).borrow_mut().send_avail = lwip_sys::tcp_sndbuf_(raw) as usize;
|
||||
let mut state = (*state).borrow_mut();
|
||||
state.send_avail = lwip_sys::tcp_sndbuf_(raw) as usize;
|
||||
state.total_sent = state.total_sent.wrapping_add(len as usize);
|
||||
}
|
||||
lwip_sys::ERR_OK
|
||||
}
|
||||
@ -463,37 +541,56 @@ impl TcpStream {
|
||||
unsafe {
|
||||
let mut state = Box::new(RefCell::new(TcpStreamState {
|
||||
recv_buffer: LinkedList::new(),
|
||||
send_avail: lwip_sys::tcp_sndbuf_(raw) as usize
|
||||
send_avail: lwip_sys::tcp_sndbuf_(raw) as usize,
|
||||
total_sent: 0
|
||||
}));
|
||||
let arg = &mut *state as *mut RefCell<TcpStreamState> as *mut _;
|
||||
lwip_sys::tcp_arg(raw, arg);
|
||||
lwip_sys::tcp_recv(raw, recv);
|
||||
lwip_sys::tcp_sent(raw, sent);
|
||||
lwip_sys::tcp_err(raw, err);
|
||||
lwip_sys::tcp_recv(raw, Some(recv));
|
||||
lwip_sys::tcp_sent(raw, Some(sent));
|
||||
lwip_sys::tcp_err(raw, Some(err));
|
||||
lwip_sys::tcp_nagle_disable_(raw);
|
||||
TcpStream { raw: raw, state: state }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn state(&self) -> *const RefCell<TcpStreamState> {
|
||||
pub fn state(&self) -> &RefCell<TcpStreamState> {
|
||||
&*self.state
|
||||
}
|
||||
|
||||
pub fn write(&self, data: &[u8]) -> Result<usize> {
|
||||
let sndbuf = unsafe { lwip_sys::tcp_sndbuf_(self.raw) } as usize;
|
||||
unsafe fn write_common(&self, data: &[u8], copy: bool) -> Result<usize> {
|
||||
let sndbuf = lwip_sys::tcp_sndbuf_(self.raw) as usize;
|
||||
let len = if data.len() < sndbuf { data.len() } else { sndbuf };
|
||||
let result = result_from(unsafe {
|
||||
let result = result_from({
|
||||
lwip_sys::tcp_write(self.raw, data as *const [u8] as *const _, len as u16,
|
||||
lwip_sys::TCP_WRITE_FLAG_COPY |
|
||||
lwip_sys::TCP_WRITE_FLAG_MORE)
|
||||
lwip_sys::TCP_WRITE_FLAG_MORE |
|
||||
if copy { lwip_sys::TCP_WRITE_FLAG_COPY } else { 0 })
|
||||
}, || len);
|
||||
self.state.borrow_mut().send_avail = unsafe { lwip_sys::tcp_sndbuf_(self.raw) } as usize;
|
||||
self.state.borrow_mut().send_avail = lwip_sys::tcp_sndbuf_(self.raw) as usize;
|
||||
result
|
||||
}
|
||||
|
||||
pub fn write(&self, data: &[u8]) -> Result<usize> {
|
||||
unsafe { self.write_common(data, true) }
|
||||
}
|
||||
|
||||
pub fn write_in_place<F>(&self, data: &[u8], mut relinquish: F) -> Result<usize>
|
||||
where F: FnMut() -> Result<()> {
|
||||
let cursor = self.state.borrow().total_sent;
|
||||
let written = try!(unsafe { self.write_common(data, false) });
|
||||
loop {
|
||||
let cursor_now = self.state.borrow().total_sent;
|
||||
if cursor_now >= cursor.wrapping_add(written) {
|
||||
return Ok(written)
|
||||
} else {
|
||||
try!(relinquish())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flush(&self) -> Result<()> {
|
||||
const EMPTY_DATA: [u8; 0] = [];
|
||||
result_from(unsafe {
|
||||
lwip_sys::tcp_write(self.raw, &EMPTY_DATA as *const [u8] as *const _, 0, 0)
|
||||
lwip_sys::tcp_write(self.raw, ptr::null(), 0, 0)
|
||||
}, || ())
|
||||
}
|
||||
|
||||
@ -505,7 +602,10 @@ impl TcpStream {
|
||||
Some(_) => ()
|
||||
}
|
||||
match state.recv_buffer.pop_front() {
|
||||
Some(Ok(pbuf)) => return Ok(Some(pbuf)),
|
||||
Some(Ok(pbuf)) => {
|
||||
unsafe { lwip_sys::tcp_recved(self.raw, pbuf.len() as u16) }
|
||||
return Ok(Some(pbuf))
|
||||
},
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
@ -533,6 +633,11 @@ impl TcpStream {
|
||||
impl Drop for TcpStream {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// lwip *will* try to call back after tcp_close
|
||||
lwip_sys::tcp_recv(self.raw, None);
|
||||
lwip_sys::tcp_sent(self.raw, None);
|
||||
lwip_sys::tcp_err(self.raw, None);
|
||||
|
||||
// tcp_close can fail here, but in drop() we don't care
|
||||
let _ = lwip_sys::tcp_close(self.raw);
|
||||
}
|
||||
|
@ -9,3 +9,6 @@ path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
alloc_artiq = { path = "../liballoc_artiq" }
|
||||
|
||||
[features]
|
||||
alloc = []
|
||||
|
@ -17,6 +17,8 @@ use core::cmp;
|
||||
use core::fmt;
|
||||
use io::{self, DEFAULT_BUF_SIZE, Error, ErrorKind, SeekFrom};
|
||||
use io::memchr;
|
||||
use alloc::boxed::Box;
|
||||
use collections::vec::Vec;
|
||||
|
||||
/// The `BufReader` struct adds buffering to any reader.
|
||||
///
|
||||
|
@ -13,6 +13,8 @@ use io::prelude::*;
|
||||
|
||||
use core::cmp;
|
||||
use io::{self, SeekFrom, Error, ErrorKind};
|
||||
use alloc::boxed::Box;
|
||||
use collections::vec::Vec;
|
||||
|
||||
/// A `Cursor` wraps another type and provides it with a
|
||||
/// [`Seek`](trait.Seek.html) implementation.
|
||||
|
@ -8,7 +8,8 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use alloc::boxed::Box;
|
||||
#[cfg(feature="alloc")] use alloc::boxed::Box;
|
||||
#[cfg(not(feature="alloc"))] use ::FakeBox as Box;
|
||||
use core::convert::Into;
|
||||
use core::fmt;
|
||||
use core::marker::{Send, Sync};
|
||||
@ -62,13 +63,19 @@ pub struct Error {
|
||||
enum Repr {
|
||||
Os(i32),
|
||||
|
||||
#[cfg(feature="alloc")]
|
||||
Custom(Box<Custom>),
|
||||
#[cfg(not(feature="alloc"))]
|
||||
Custom(Custom),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Custom {
|
||||
kind: ErrorKind,
|
||||
#[cfg(feature="alloc")]
|
||||
error: Box<error::Error+Send+Sync>,
|
||||
#[cfg(not(feature="alloc"))]
|
||||
error: &'static str
|
||||
}
|
||||
|
||||
/// A list specifying general categories of I/O error.
|
||||
@ -162,12 +169,21 @@ impl Error {
|
||||
/// // errors can also be created from other errors
|
||||
/// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error);
|
||||
/// ```
|
||||
#[cfg(feature="alloc")]
|
||||
pub fn new<E>(kind: ErrorKind, error: E) -> Error
|
||||
where E: Into<Box<error::Error+Send+Sync>>
|
||||
{
|
||||
Self::_new(kind, error.into())
|
||||
}
|
||||
|
||||
#[cfg(not(feature="alloc"))]
|
||||
pub fn new<E>(kind: ErrorKind, error: E) -> Error
|
||||
where E: Into<&'static str>
|
||||
{
|
||||
Self::_new(kind, error.into())
|
||||
}
|
||||
|
||||
#[cfg(feature="alloc")]
|
||||
fn _new(kind: ErrorKind, error: Box<error::Error+Send+Sync>) -> Error {
|
||||
Error {
|
||||
repr: Repr::Custom(Box::new(Custom {
|
||||
@ -177,6 +193,16 @@ impl Error {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature="alloc"))]
|
||||
fn _new(kind: ErrorKind, error: &'static str) -> Error {
|
||||
Error {
|
||||
repr: Repr::Custom(Box::new(Custom {
|
||||
kind: kind,
|
||||
error: error,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new instance of an `Error` from a particular OS error code.
|
||||
pub fn from_raw_os_error(code: i32) -> Error {
|
||||
Error { repr: Repr::Os(code) }
|
||||
@ -198,6 +224,7 @@ impl Error {
|
||||
///
|
||||
/// If this `Error` was constructed via `new` then this function will
|
||||
/// return `Some`, otherwise it will return `None`.
|
||||
#[cfg(feature="alloc")]
|
||||
pub fn get_ref(&self) -> Option<&(error::Error+Send+Sync+'static)> {
|
||||
match self.repr {
|
||||
Repr::Os(..) => None,
|
||||
@ -210,6 +237,7 @@ impl Error {
|
||||
///
|
||||
/// If this `Error` was constructed via `new` then this function will
|
||||
/// return `Some`, otherwise it will return `None`.
|
||||
#[cfg(feature="alloc")]
|
||||
pub fn get_mut(&mut self) -> Option<&mut (error::Error+Send+Sync+'static)> {
|
||||
match self.repr {
|
||||
Repr::Os(..) => None,
|
||||
@ -221,6 +249,7 @@ impl Error {
|
||||
///
|
||||
/// If this `Error` was constructed via `new` then this function will
|
||||
/// return `Some`, otherwise it will return `None`.
|
||||
#[cfg(feature="alloc")]
|
||||
pub fn into_inner(self) -> Option<Box<error::Error+Send+Sync>> {
|
||||
match self.repr {
|
||||
Repr::Os(..) => None,
|
||||
@ -282,14 +311,24 @@ impl error::Error for Error {
|
||||
ErrorKind::UnexpectedEof => "unexpected end of file",
|
||||
ErrorKind::__Nonexhaustive => unreachable!()
|
||||
},
|
||||
Repr::Custom(ref c) => c.error.description(),
|
||||
Repr::Custom(ref c) => {
|
||||
#[cfg(feature="alloc")]
|
||||
{ c.error.description() }
|
||||
#[cfg(not(feature="alloc"))]
|
||||
{ c.error }
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&error::Error> {
|
||||
match self.repr {
|
||||
Repr::Os(..) => None,
|
||||
Repr::Custom(ref c) => c.error.cause(),
|
||||
Repr::Custom(ref _c) => {
|
||||
#[cfg(feature="alloc")]
|
||||
{ _c.error.cause() }
|
||||
#[cfg(not(feature="alloc"))]
|
||||
{ None }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,3 @@
|
||||
|
||||
pub use super::{Read, Write, Seek};
|
||||
pub use super::BufRead;
|
||||
|
||||
pub use alloc::boxed::Box;
|
||||
pub use collections::vec::Vec;
|
||||
|
@ -1,6 +1,7 @@
|
||||
#![feature(lang_items, asm, alloc, collections, libc, needs_panic_runtime,
|
||||
question_mark, unicode, reflect_marker, raw, int_error_internals,
|
||||
try_from, try_borrow)]
|
||||
try_from, try_borrow, macro_reexport, allow_internal_unstable,
|
||||
stmt_expr_attributes)]
|
||||
#![no_std]
|
||||
#![needs_panic_runtime]
|
||||
|
||||
@ -8,6 +9,7 @@ extern crate rustc_unicode;
|
||||
extern crate alloc_artiq;
|
||||
extern crate alloc;
|
||||
#[macro_use]
|
||||
#[macro_reexport(vec, format)]
|
||||
extern crate collections;
|
||||
extern crate libc;
|
||||
|
||||
@ -21,62 +23,22 @@ pub use collections::{binary_heap, borrow, boxed, btree_map, btree_set, fmt, lin
|
||||
pub mod prelude {
|
||||
pub mod v1 {
|
||||
pub use core::prelude::v1::*;
|
||||
pub use collections::*;
|
||||
pub use io::{Read, Write, Seek};
|
||||
pub use io::BufRead;
|
||||
pub use collections::boxed::Box;
|
||||
pub use collections::borrow::ToOwned;
|
||||
pub use collections::string::{String, ToString};
|
||||
pub use collections::vec::Vec;
|
||||
}
|
||||
}
|
||||
|
||||
pub mod time;
|
||||
pub mod error;
|
||||
pub mod io;
|
||||
|
||||
use core::fmt::Write;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => ($crate::print_fmt(format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
($fmt:expr) => (print!(concat!($fmt, "\n")));
|
||||
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
|
||||
}
|
||||
|
||||
extern {
|
||||
fn putchar(c: libc::c_int) -> libc::c_int;
|
||||
fn readchar() -> libc::c_char;
|
||||
}
|
||||
|
||||
pub struct Console;
|
||||
|
||||
impl core::fmt::Write for Console {
|
||||
fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
|
||||
for c in s.bytes() { unsafe { putchar(c as i32); } }
|
||||
Ok(())
|
||||
// Provide Box::new wrapper
|
||||
#[cfg(not(feature="alloc"))]
|
||||
struct FakeBox<T>(core::marker::PhantomData<T>);
|
||||
#[cfg(not(feature="alloc"))]
|
||||
impl<T> FakeBox<T> {
|
||||
fn new(val: T) -> T {
|
||||
val
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_fmt(args: self::core::fmt::Arguments) {
|
||||
let _ = Console.write_fmt(args);
|
||||
}
|
||||
|
||||
#[lang = "panic_fmt"]
|
||||
extern fn panic_fmt(args: self::core::fmt::Arguments, file: &'static str, line: u32) -> ! {
|
||||
let _ = write!(Console, "panic at {}:{}: ", file, line);
|
||||
let _ = Console.write_fmt(args);
|
||||
let _ = write!(Console, "\nwaiting for debugger...\n");
|
||||
unsafe {
|
||||
let _ = readchar();
|
||||
loop { asm!("l.trap 0") }
|
||||
}
|
||||
}
|
||||
|
||||
// Allow linking with crates that are built as -Cpanic=unwind even when the root crate
|
||||
// is built with -Cpanic=abort.
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn _Unwind_Resume() -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
@ -1,117 +0,0 @@
|
||||
use core::ops::{Add, Sub, Mul, Div};
|
||||
|
||||
const MILLIS_PER_SEC: u64 = 1_000;
|
||||
const NANOS_PER_MILLI: u32 = 1_000_000;
|
||||
|
||||
/// A duration type to represent a span of time, typically used for system
|
||||
/// timeouts.
|
||||
///
|
||||
/// Each duration is composed of a number of seconds and nanosecond precision.
|
||||
/// APIs binding a system timeout will typically round up the nanosecond
|
||||
/// precision if the underlying system does not support that level of precision.
|
||||
///
|
||||
/// Durations implement many common traits, including `Add`, `Sub`, and other
|
||||
/// ops traits. Currently a duration may only be inspected for its number of
|
||||
/// seconds and its nanosecond precision.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let five_seconds = Duration::new(5, 0);
|
||||
/// let five_seconds_and_five_nanos = five_seconds + Duration::new(0, 5);
|
||||
///
|
||||
/// assert_eq!(five_seconds_and_five_nanos.as_secs(), 5);
|
||||
/// assert_eq!(five_seconds_and_five_nanos.subsec_nanos(), 5);
|
||||
///
|
||||
/// let ten_millis = Duration::from_millis(10);
|
||||
/// ```
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
pub struct Duration {
|
||||
millis: u64
|
||||
}
|
||||
|
||||
impl Duration {
|
||||
/// Creates a new `Duration` from the specified number of seconds and
|
||||
/// additional nanosecond precision.
|
||||
///
|
||||
/// If the nanoseconds is greater than 1 billion (the number of nanoseconds
|
||||
/// in a second), then it will carry over into the seconds provided.
|
||||
pub fn new(secs: u64, nanos: u32) -> Duration {
|
||||
Duration { millis: secs * MILLIS_PER_SEC + (nanos / NANOS_PER_MILLI) as u64 }
|
||||
}
|
||||
|
||||
/// Creates a new `Duration` from the specified number of seconds.
|
||||
pub fn from_secs(secs: u64) -> Duration {
|
||||
Duration { millis: secs * MILLIS_PER_SEC }
|
||||
}
|
||||
|
||||
/// Creates a new `Duration` from the specified number of milliseconds.
|
||||
pub fn from_millis(millis: u64) -> Duration {
|
||||
Duration { millis: millis }
|
||||
}
|
||||
|
||||
/// Returns the number of whole milliseconds represented by this duration.
|
||||
pub fn as_millis(&self) -> u64 { self.millis }
|
||||
|
||||
/// Returns the number of whole seconds represented by this duration.
|
||||
///
|
||||
/// The extra precision represented by this duration is ignored (e.g. extra
|
||||
/// nanoseconds are not represented in the returned value).
|
||||
pub fn as_secs(&self) -> u64 {
|
||||
self.millis / MILLIS_PER_SEC
|
||||
}
|
||||
|
||||
/// Returns the nanosecond precision represented by this duration.
|
||||
///
|
||||
/// This method does **not** return the length of the duration when
|
||||
/// represented by nanoseconds. The returned number always represents a
|
||||
/// fractional portion of a second (e.g. it is less than one billion).
|
||||
pub fn subsec_nanos(&self) -> u32 {
|
||||
(self.millis % MILLIS_PER_SEC) as u32 * NANOS_PER_MILLI
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Duration {
|
||||
type Output = Duration;
|
||||
|
||||
fn add(self, rhs: Duration) -> Duration {
|
||||
Duration {
|
||||
millis: self.millis.checked_add(rhs.millis)
|
||||
.expect("overflow when adding durations")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Duration {
|
||||
type Output = Duration;
|
||||
|
||||
fn sub(self, rhs: Duration) -> Duration {
|
||||
Duration {
|
||||
millis: self.millis.checked_sub(rhs.millis)
|
||||
.expect("overflow when subtracting durations")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<u32> for Duration {
|
||||
type Output = Duration;
|
||||
|
||||
fn mul(self, rhs: u32) -> Duration {
|
||||
Duration {
|
||||
millis: self.millis.checked_mul(rhs as u64)
|
||||
.expect("overflow when multiplying duration")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<u32> for Duration {
|
||||
type Output = Duration;
|
||||
|
||||
fn div(self, rhs: u32) -> Duration {
|
||||
Duration {
|
||||
millis: self.millis / (rhs as u64)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
use core::ops::{Add, Sub};
|
||||
use time::duration::Duration;
|
||||
|
||||
/// A measurement of a monotonically increasing clock.
|
||||
///
|
||||
/// Instants are always guaranteed to be greater than any previously measured
|
||||
/// instant when created, and are often useful for tasks such as measuring
|
||||
/// benchmarks or timing how long an operation takes.
|
||||
///
|
||||
/// Note, however, that instants are not guaranteed to be **steady**. In other
|
||||
/// words, each tick of the underlying clock may not be the same length (e.g.
|
||||
/// some seconds may be longer than others). An instant may jump forwards or
|
||||
/// experience time dilation (slow down or speed up), but it will never go
|
||||
/// backwards.
|
||||
///
|
||||
/// Instants are opaque types that can only be compared to one another. There is
|
||||
/// no method to get "the number of seconds" from an instant. Instead, it only
|
||||
/// allows measuring the duration between two instants (or comparing two
|
||||
/// instants).
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Instant {
|
||||
millis: u64
|
||||
}
|
||||
|
||||
impl Instant {
|
||||
/// Returns an instant corresponding to "now".
|
||||
pub fn now() -> Instant {
|
||||
extern {
|
||||
fn clock_get_ms() -> i64;
|
||||
}
|
||||
|
||||
Instant { millis: unsafe { clock_get_ms() as u64 } }
|
||||
}
|
||||
|
||||
/// Returns the amount of time elapsed from another instant to this one.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if `earlier` is later than `self`, which should
|
||||
/// only be possible if `earlier` was created after `self`. Because
|
||||
/// `Instant` is monotonic, the only time that this should happen should be
|
||||
/// a bug.
|
||||
pub fn duration_from_earlier(&self, earlier: Instant) -> Duration {
|
||||
let millis = self.millis.checked_sub(earlier.millis)
|
||||
.expect("`earlier` is later than `self`");
|
||||
Duration::from_millis(millis)
|
||||
}
|
||||
|
||||
/// Returns the amount of time elapsed since this instant was created.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function may panic if the current time is earlier than this
|
||||
/// instant, which is something that can happen if an `Instant` is
|
||||
/// produced synthetically.
|
||||
pub fn elapsed(&self) -> Duration {
|
||||
Instant::now().duration_from_earlier(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Duration> for Instant {
|
||||
type Output = Instant;
|
||||
|
||||
fn add(self, other: Duration) -> Instant {
|
||||
Instant {
|
||||
millis: self.millis.checked_add(other.as_millis())
|
||||
.expect("overflow when adding duration to instant")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<Duration> for Instant {
|
||||
type Output = Instant;
|
||||
|
||||
fn sub(self, other: Duration) -> Instant {
|
||||
Instant {
|
||||
millis: self.millis.checked_sub(other.as_millis())
|
||||
.expect("overflow when subtracting duration from instant")
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
pub use self::duration::Duration;
|
||||
pub use self::instant::Instant;
|
||||
|
||||
mod duration;
|
||||
mod instant;
|
92
artiq/runtime.rs/src/analyzer.rs
Normal file
92
artiq/runtime.rs/src/analyzer.rs
Normal file
@ -0,0 +1,92 @@
|
||||
use std::io::{self, Write};
|
||||
use board::{self, csr};
|
||||
use sched::{Waiter, Spawner};
|
||||
use sched::{TcpListener, TcpStream, SocketAddr, IP_ANY};
|
||||
use analyzer_proto::*;
|
||||
|
||||
const BUFFER_SIZE: usize = 512 * 1024;
|
||||
|
||||
// hack until https://github.com/rust-lang/rust/issues/33626 is fixed
|
||||
#[repr(simd)]
|
||||
struct Align64(u64, u64, u64, u64, u64, u64, u64, u64);
|
||||
|
||||
struct Buffer {
|
||||
data: [u8; BUFFER_SIZE],
|
||||
__alignment: [Align64; 0]
|
||||
}
|
||||
|
||||
static mut BUFFER: Buffer = Buffer {
|
||||
data: [0; BUFFER_SIZE],
|
||||
__alignment: []
|
||||
};
|
||||
|
||||
fn arm() {
|
||||
unsafe {
|
||||
let base_addr = &mut BUFFER.data[0] as *mut _ as usize;
|
||||
let last_addr = &mut BUFFER.data[BUFFER_SIZE - 1] as *mut _ as usize;
|
||||
csr::rtio_analyzer::message_encoder_overflow_reset_write(1);
|
||||
csr::rtio_analyzer::dma_base_address_write(base_addr as u64);
|
||||
csr::rtio_analyzer::dma_last_address_write(last_addr as u64);
|
||||
csr::rtio_analyzer::dma_reset_write(1);
|
||||
csr::rtio_analyzer::enable_write(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn disarm() {
|
||||
unsafe {
|
||||
csr::rtio_analyzer::enable_write(0);
|
||||
while csr::rtio_analyzer::busy_read() != 0 {}
|
||||
board::flush_cpu_dcache();
|
||||
board::flush_l2_cache();
|
||||
}
|
||||
}
|
||||
|
||||
fn worker(mut stream: TcpStream) -> io::Result<()> {
|
||||
let data = unsafe { &BUFFER.data[..] };
|
||||
let overflow_occurred = unsafe { csr::rtio_analyzer::message_encoder_overflow_read() != 0 };
|
||||
let total_byte_count = unsafe { csr::rtio_analyzer::dma_byte_count_read() };
|
||||
let pointer = (total_byte_count % BUFFER_SIZE as u64) as usize;
|
||||
let wraparound = total_byte_count >= BUFFER_SIZE as u64;
|
||||
|
||||
let header = Header {
|
||||
total_byte_count: total_byte_count,
|
||||
sent_bytes: if wraparound { BUFFER_SIZE as u32 } else { total_byte_count as u32 },
|
||||
overflow_occurred: overflow_occurred,
|
||||
log_channel: csr::CONFIG_RTIO_LOG_CHANNEL as u8,
|
||||
dds_onehot_sel: csr::CONFIG_DDS_ONEHOT_SEL != 0
|
||||
};
|
||||
trace!("{:?}", header);
|
||||
|
||||
try!(header.write_to(&mut stream));
|
||||
if wraparound {
|
||||
try!(stream.write(&data[pointer..]));
|
||||
try!(stream.write(&data[..pointer]));
|
||||
} else {
|
||||
try!(stream.write(&data[..pointer]));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn thread(waiter: Waiter, _spawner: Spawner) {
|
||||
// verify that the hack above works
|
||||
assert!(::core::mem::align_of::<Buffer>() == 64);
|
||||
|
||||
let addr = SocketAddr::new(IP_ANY, 1382);
|
||||
let listener = TcpListener::bind(waiter, addr).expect("cannot bind socket");
|
||||
listener.set_keepalive(true);
|
||||
|
||||
loop {
|
||||
arm();
|
||||
|
||||
let (stream, addr) = listener.accept().expect("cannot accept client");
|
||||
info!("connection from {}", addr);
|
||||
|
||||
disarm();
|
||||
|
||||
match worker(stream) {
|
||||
Ok(()) => (),
|
||||
Err(err) => error!("analyzer aborted: {}", err)
|
||||
}
|
||||
}
|
||||
}
|
22
artiq/runtime.rs/src/analyzer_proto.rs
Normal file
22
artiq/runtime.rs/src/analyzer_proto.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use std::io::{self, Write};
|
||||
use proto::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Header {
|
||||
pub sent_bytes: u32,
|
||||
pub total_byte_count: u64,
|
||||
pub overflow_occurred: bool,
|
||||
pub log_channel: u8,
|
||||
pub dds_onehot_sel: bool
|
||||
}
|
||||
|
||||
impl Header {
|
||||
pub fn write_to(&self, writer: &mut Write) -> io::Result<()> {
|
||||
try!(write_u32(writer, self.sent_bytes));
|
||||
try!(write_u64(writer, self.total_byte_count));
|
||||
try!(write_u8(writer, self.overflow_occurred as u8));
|
||||
try!(write_u8(writer, self.log_channel));
|
||||
try!(write_u8(writer, self.dds_onehot_sel as u8));
|
||||
Ok(())
|
||||
}
|
||||
}
|
145
artiq/runtime.rs/src/board.rs
Normal file
145
artiq/runtime.rs/src/board.rs
Normal file
@ -0,0 +1,145 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use core::{cmp, ptr, str};
|
||||
|
||||
include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/mem.rs"));
|
||||
include!(concat!(env!("BUILDINC_DIRECTORY"), "/generated/csr.rs"));
|
||||
|
||||
pub mod spr {
|
||||
pub unsafe fn mfspr(reg: u32) -> u32 {
|
||||
let value: u32;
|
||||
asm!("l.mfspr $0, $1, 0" : "=r"(value) : "r"(reg) : : "volatile");
|
||||
value
|
||||
}
|
||||
|
||||
pub unsafe fn mtspr(reg: u32, value: u32) {
|
||||
asm!("l.mtspr $0, $1, 0" : : "r"(reg), "r"(value) : : "volatile")
|
||||
}
|
||||
|
||||
/* Definition of special-purpose registers (SPRs). */
|
||||
|
||||
pub const MAX_GRPS: u32 = 32;
|
||||
pub const MAX_SPRS_PER_GRP_BITS: u32 = 11;
|
||||
pub const MAX_SPRS_PER_GRP: u32 = 1 << MAX_SPRS_PER_GRP_BITS;
|
||||
pub const MAX_SPRS: u32 = 0x10000;
|
||||
|
||||
/* Base addresses for the groups */
|
||||
pub const SPRGROUP_SYS: u32 = 0 << MAX_SPRS_PER_GRP_BITS;
|
||||
pub const SPRGROUP_DMMU: u32 = 1 << MAX_SPRS_PER_GRP_BITS;
|
||||
pub const SPRGROUP_IMMU: u32 = 2 << MAX_SPRS_PER_GRP_BITS;
|
||||
pub const SPRGROUP_DC: u32 = 3 << MAX_SPRS_PER_GRP_BITS;
|
||||
pub const SPRGROUP_IC: u32 = 4 << MAX_SPRS_PER_GRP_BITS;
|
||||
pub const SPRGROUP_MAC: u32 = 5 << MAX_SPRS_PER_GRP_BITS;
|
||||
pub const SPRGROUP_D: u32 = 6 << MAX_SPRS_PER_GRP_BITS;
|
||||
pub const SPRGROUP_PC: u32 = 7 << MAX_SPRS_PER_GRP_BITS;
|
||||
pub const SPRGROUP_PM: u32 = 8 << MAX_SPRS_PER_GRP_BITS;
|
||||
pub const SPRGROUP_PIC: u32 = 9 << MAX_SPRS_PER_GRP_BITS;
|
||||
pub const SPRGROUP_TT: u32 = 10 << MAX_SPRS_PER_GRP_BITS;
|
||||
pub const SPRGROUP_FP: u32 = 11 << MAX_SPRS_PER_GRP_BITS;
|
||||
|
||||
/* System control and status group */
|
||||
pub const SPR_VR: u32 = SPRGROUP_SYS + 0;
|
||||
pub const SPR_UPR: u32 = SPRGROUP_SYS + 1;
|
||||
pub const SPR_CPUCFGR: u32 = SPRGROUP_SYS + 2;
|
||||
pub const SPR_DMMUCFGR: u32 = SPRGROUP_SYS + 3;
|
||||
pub const SPR_IMMUCFGR: u32 = SPRGROUP_SYS + 4;
|
||||
pub const SPR_DCCFGR: u32 = SPRGROUP_SYS + 5;
|
||||
pub const SPR_ICCFGR: u32 = SPRGROUP_SYS + 6;
|
||||
pub const SPR_DCFGR: u32 = SPRGROUP_SYS + 7;
|
||||
pub const SPR_PCCFGR: u32 = SPRGROUP_SYS + 8;
|
||||
pub const SPR_VR2: u32 = SPRGROUP_SYS + 9;
|
||||
pub const SPR_AVR: u32 = SPRGROUP_SYS + 10;
|
||||
pub const SPR_EVBAR: u32 = SPRGROUP_SYS + 11;
|
||||
pub const SPR_AECR: u32 = SPRGROUP_SYS + 12;
|
||||
pub const SPR_AESR: u32 = SPRGROUP_SYS + 13;
|
||||
pub const SPR_NPC: u32 = SPRGROUP_SYS + 16; /* CZ 21/06/01 */
|
||||
pub const SPR_SR: u32 = SPRGROUP_SYS + 17; /* CZ 21/06/01 */
|
||||
pub const SPR_PPC: u32 = SPRGROUP_SYS + 18; /* CZ 21/06/01 */
|
||||
pub const SPR_FPCSR: u32 = SPRGROUP_SYS + 20; /* CZ 21/06/01 */
|
||||
pub const SPR_ISR_BASE: u32 = SPRGROUP_SYS + 21;
|
||||
pub const SPR_EPCR_BASE: u32 = SPRGROUP_SYS + 32; /* CZ 21/06/01 */
|
||||
pub const SPR_EPCR_LAST: u32 = SPRGROUP_SYS + 47; /* CZ 21/06/01 */
|
||||
pub const SPR_EEAR_BASE: u32 = SPRGROUP_SYS + 48;
|
||||
pub const SPR_EEAR_LAST: u32 = SPRGROUP_SYS + 63;
|
||||
pub const SPR_ESR_BASE: u32 = SPRGROUP_SYS + 64;
|
||||
pub const SPR_ESR_LAST: u32 = SPRGROUP_SYS + 79;
|
||||
pub const SPR_GPR_BASE: u32 = SPRGROUP_SYS + 1024;
|
||||
|
||||
// [snip]
|
||||
|
||||
/* PIC group */
|
||||
pub const SPR_PICMR: u32 = SPRGROUP_PIC + 0;
|
||||
pub const SPR_PICPR: u32 = SPRGROUP_PIC + 1;
|
||||
pub const SPR_PICSR: u32 = SPRGROUP_PIC + 2;
|
||||
|
||||
// [snip]
|
||||
|
||||
/*
|
||||
* Bit definitions for the Supervision Register
|
||||
*
|
||||
*/
|
||||
pub const SPR_SR_SM: u32 = 0x00000001; /* Supervisor Mode */
|
||||
pub const SPR_SR_TEE: u32 = 0x00000002; /* Tick timer Exception Enable */
|
||||
pub const SPR_SR_IEE: u32 = 0x00000004; /* Interrupt Exception Enable */
|
||||
pub const SPR_SR_DCE: u32 = 0x00000008; /* Data Cache Enable */
|
||||
pub const SPR_SR_ICE: u32 = 0x00000010; /* Instruction Cache Enable */
|
||||
pub const SPR_SR_DME: u32 = 0x00000020; /* Data MMU Enable */
|
||||
pub const SPR_SR_IME: u32 = 0x00000040; /* Instruction MMU Enable */
|
||||
pub const SPR_SR_LEE: u32 = 0x00000080; /* Little Endian Enable */
|
||||
pub const SPR_SR_CE: u32 = 0x00000100; /* CID Enable */
|
||||
pub const SPR_SR_F: u32 = 0x00000200; /* Condition Flag */
|
||||
pub const SPR_SR_CY: u32 = 0x00000400; /* Carry flag */
|
||||
pub const SPR_SR_OV: u32 = 0x00000800; /* Overflow flag */
|
||||
pub const SPR_SR_OVE: u32 = 0x00001000; /* Overflow flag Exception */
|
||||
pub const SPR_SR_DSX: u32 = 0x00002000; /* Delay Slot Exception */
|
||||
pub const SPR_SR_EPH: u32 = 0x00004000; /* Exception Prefix High */
|
||||
pub const SPR_SR_FO: u32 = 0x00008000; /* Fixed one */
|
||||
pub const SPR_SR_SUMRA: u32 = 0x00010000; /* Supervisor SPR read access */
|
||||
pub const SPR_SR_RES: u32 = 0x0ffe0000; /* Reserved */
|
||||
pub const SPR_SR_CID: u32 = 0xf0000000; /* Context ID */
|
||||
|
||||
}
|
||||
|
||||
pub mod irq {
|
||||
use super::spr::*;
|
||||
|
||||
pub fn get_ie() -> bool {
|
||||
unsafe { mfspr(SPR_SR) & SPR_SR_IEE != 0 }
|
||||
}
|
||||
|
||||
pub fn set_ie(ie: bool) {
|
||||
if ie {
|
||||
unsafe { mtspr(SPR_SR, mfspr(SPR_SR) | SPR_SR_IEE) }
|
||||
} else {
|
||||
unsafe { mtspr(SPR_SR, mfspr(SPR_SR) & !SPR_SR_IEE) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mask() -> u32 {
|
||||
unsafe { mfspr(SPR_PICMR) }
|
||||
}
|
||||
|
||||
pub fn set_mask(mask: u32) {
|
||||
unsafe { mtspr(SPR_PICMR, mask) }
|
||||
}
|
||||
|
||||
pub fn pending() -> u32 {
|
||||
unsafe { mfspr(SPR_PICSR) }
|
||||
}
|
||||
}
|
||||
|
||||
extern {
|
||||
pub fn flush_cpu_dcache();
|
||||
pub fn flush_l2_cache();
|
||||
}
|
||||
|
||||
pub fn ident(buf: &mut [u8]) -> &str {
|
||||
unsafe {
|
||||
let len = ptr::read_volatile(csr::IDENTIFIER_MEM_BASE);
|
||||
let len = cmp::min(len as usize, buf.len());
|
||||
for i in 0..len {
|
||||
buf[i] = ptr::read_volatile(csr::IDENTIFIER_MEM_BASE.offset(1 + i as isize)) as u8
|
||||
}
|
||||
str::from_utf8_unchecked(&buf[..len])
|
||||
}
|
||||
}
|
53
artiq/runtime.rs/src/cache.rs
Normal file
53
artiq/runtime.rs/src/cache.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use std::vec::Vec;
|
||||
use std::string::String;
|
||||
use std::btree_map::BTreeMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Entry {
|
||||
data: Vec<u32>,
|
||||
borrowed: bool
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Cache {
|
||||
entries: BTreeMap<String, Entry>
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
pub fn new() -> Cache {
|
||||
Cache { entries: BTreeMap::new() }
|
||||
}
|
||||
|
||||
pub fn get(&mut self, key: &str) -> *const [u32] {
|
||||
match self.entries.get_mut(key) {
|
||||
None => &[],
|
||||
Some(ref mut entry) => {
|
||||
entry.borrowed = true;
|
||||
&entry.data[..]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put(&mut self, key: &str, data: &[u32]) -> Result<(), ()> {
|
||||
match self.entries.get_mut(key) {
|
||||
None => (),
|
||||
Some(ref mut entry) => {
|
||||
if entry.borrowed { return Err(()) }
|
||||
entry.data = Vec::from(data);
|
||||
return Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
self.entries.insert(String::from(key), Entry {
|
||||
data: Vec::from(data),
|
||||
borrowed: false
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub unsafe fn unborrow(&mut self) {
|
||||
for (_key, entry) in self.entries.iter_mut() {
|
||||
entry.borrowed = false;
|
||||
}
|
||||
}
|
||||
}
|
83
artiq/runtime.rs/src/clock.rs
Normal file
83
artiq/runtime.rs/src/clock.rs
Normal file
@ -0,0 +1,83 @@
|
||||
use board::csr;
|
||||
|
||||
const INIT: u64 = ::core::i64::MAX as u64;
|
||||
const FREQ: u64 = ::board::csr::CONFIG_CLOCK_FREQUENCY as u64;
|
||||
|
||||
pub fn init() {
|
||||
unsafe {
|
||||
csr::timer0::en_write(0);
|
||||
csr::timer0::load_write(INIT);
|
||||
csr::timer0::reload_write(INIT);
|
||||
csr::timer0::en_write(1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_us() -> u64 {
|
||||
unsafe {
|
||||
csr::timer0::update_value_write(1);
|
||||
(INIT - csr::timer0::value_read()) / (FREQ / 1_000_000)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ms() -> u64 {
|
||||
unsafe {
|
||||
csr::timer0::update_value_write(1);
|
||||
(INIT - csr::timer0::value_read()) / (FREQ / 1_000)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spin_us(interval: u64) {
|
||||
unsafe {
|
||||
csr::timer0::update_value_write(1);
|
||||
let threshold = csr::timer0::value_read() - interval * (FREQ / 1_000_000);
|
||||
while csr::timer0::value_read() > threshold {
|
||||
csr::timer0::update_value_write(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Watchdog {
|
||||
active: bool,
|
||||
threshold: u64
|
||||
}
|
||||
|
||||
pub const MAX_WATCHDOGS: usize = 16;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WatchdogSet {
|
||||
watchdogs: [Watchdog; MAX_WATCHDOGS]
|
||||
}
|
||||
|
||||
impl WatchdogSet {
|
||||
pub fn new() -> WatchdogSet {
|
||||
WatchdogSet {
|
||||
watchdogs: [Watchdog { active: false, threshold: 0 }; MAX_WATCHDOGS]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_ms(&mut self, interval: u64) -> Result<usize, ()> {
|
||||
for (index, watchdog) in self.watchdogs.iter_mut().enumerate() {
|
||||
if !watchdog.active {
|
||||
watchdog.active = true;
|
||||
watchdog.threshold = get_ms() + interval;
|
||||
return Ok(index)
|
||||
}
|
||||
}
|
||||
|
||||
Err(())
|
||||
}
|
||||
|
||||
pub fn clear(&mut self, index: usize) {
|
||||
if index < MAX_WATCHDOGS {
|
||||
self.watchdogs[index].active = false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expired(&self) -> bool {
|
||||
self.watchdogs.iter()
|
||||
.filter(|wd| wd.active)
|
||||
.min_by_key(|wd| wd.threshold)
|
||||
.map_or(false, |wd| get_ms() > wd.threshold)
|
||||
}
|
||||
}
|
63
artiq/runtime.rs/src/config.rs
Normal file
63
artiq/runtime.rs/src/config.rs
Normal file
@ -0,0 +1,63 @@
|
||||
use std::cmp;
|
||||
use std::vec::Vec;
|
||||
use libc::{c_void, c_char, c_int, c_uint};
|
||||
|
||||
extern {
|
||||
fn fs_remove(key: *const c_char);
|
||||
fn fs_erase();
|
||||
fn fs_write(key: *const c_char, buffer: *const c_void, buflen: c_uint) -> c_int;
|
||||
fn fs_read(key: *const c_char, buffer: *mut c_void, buflen: c_uint,
|
||||
remain: *mut c_uint) -> c_uint;
|
||||
}
|
||||
|
||||
macro_rules! c_str {
|
||||
($s:ident) => {
|
||||
{
|
||||
let mut c = [0; 64 + 1];
|
||||
let len = cmp::min($s.len(), c.len() - 1);
|
||||
c[..len].copy_from_slice($s.as_bytes());
|
||||
c
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(key: &str, buf: &mut [u8]) -> Result<usize, usize> {
|
||||
let key_c = c_str!(key);
|
||||
let mut remain: c_uint = 0;
|
||||
let result = unsafe {
|
||||
fs_read(key_c.as_ptr() as *const c_char,
|
||||
buf.as_mut_ptr() as *mut c_void, buf.len() as c_uint, &mut remain)
|
||||
};
|
||||
if remain == 0 { Ok(result as usize) } else { Err(remain as usize) }
|
||||
}
|
||||
|
||||
pub fn read_to_end(key: &str) -> Vec<u8> {
|
||||
let mut value = Vec::new();
|
||||
match read(key, &mut []) {
|
||||
Ok(0) => (),
|
||||
Ok(_) => unreachable!(),
|
||||
Err(size) => {
|
||||
value.resize(size, 0);
|
||||
read(key, &mut value).unwrap();
|
||||
}
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
pub fn write(key: &str, buf: &[u8]) -> Result<(), ()> {
|
||||
let key_c = c_str!(key);
|
||||
let result = unsafe {
|
||||
fs_write(key_c.as_ptr() as *const c_char,
|
||||
buf.as_ptr() as *mut c_void, buf.len() as c_uint)
|
||||
};
|
||||
if result == 1 { Ok(()) } else { Err(()) }
|
||||
}
|
||||
|
||||
pub fn remove(key: &str) {
|
||||
let key_c = c_str!(key);
|
||||
unsafe { fs_remove(key_c.as_ptr() as *const c_char) }
|
||||
}
|
||||
|
||||
pub fn erase() {
|
||||
unsafe { fs_erase() }
|
||||
}
|
31
artiq/runtime.rs/src/kernel.rs
Normal file
31
artiq/runtime.rs/src/kernel.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use core::ptr;
|
||||
use board::csr;
|
||||
use mailbox;
|
||||
use rpc_queue;
|
||||
|
||||
use kernel_proto::{KERNELCPU_EXEC_ADDRESS, KERNELCPU_LAST_ADDRESS, KSUPPORT_HEADER_SIZE};
|
||||
|
||||
pub unsafe fn start() {
|
||||
if csr::kernel_cpu::reset_read() == 0 {
|
||||
panic!("attempted to start kernel CPU when it is already running")
|
||||
}
|
||||
|
||||
stop();
|
||||
|
||||
let ksupport_image = include_bytes!(concat!(env!("CARGO_TARGET_DIR"), "/../ksupport.elf"));
|
||||
let ksupport_addr = (KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE) as *mut u8;
|
||||
ptr::copy_nonoverlapping(ksupport_image.as_ptr(), ksupport_addr, ksupport_image.len());
|
||||
|
||||
csr::kernel_cpu::reset_write(0);
|
||||
|
||||
rpc_queue::init();
|
||||
}
|
||||
|
||||
pub fn stop() {
|
||||
unsafe { csr::kernel_cpu::reset_write(1) }
|
||||
mailbox::acknowledge();
|
||||
}
|
||||
|
||||
pub fn validate(ptr: usize) -> bool {
|
||||
ptr >= KERNELCPU_EXEC_ADDRESS && ptr <= KERNELCPU_LAST_ADDRESS
|
||||
}
|
62
artiq/runtime.rs/src/kernel_proto.rs
Normal file
62
artiq/runtime.rs/src/kernel_proto.rs
Normal file
@ -0,0 +1,62 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use core::fmt;
|
||||
|
||||
pub const KERNELCPU_EXEC_ADDRESS: usize = 0x40800000;
|
||||
pub const KERNELCPU_PAYLOAD_ADDRESS: usize = 0x40840000;
|
||||
pub const KERNELCPU_LAST_ADDRESS: usize = 0x4fffffff;
|
||||
pub const KSUPPORT_HEADER_SIZE: usize = 0x80;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Exception<'a> {
|
||||
pub name: *const u8,
|
||||
pub file: *const u8,
|
||||
pub line: u32,
|
||||
pub column: u32,
|
||||
pub function: *const u8,
|
||||
pub message: *const u8,
|
||||
pub param: [u64; 3],
|
||||
pub phantom: PhantomData<&'a str>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Message<'a> {
|
||||
LoadRequest(&'a [u8]),
|
||||
LoadReply(Result<(), &'a str>),
|
||||
|
||||
NowInitRequest,
|
||||
NowInitReply(u64),
|
||||
NowSave(u64),
|
||||
|
||||
RunFinished,
|
||||
RunException {
|
||||
exception: Exception<'a>,
|
||||
backtrace: &'a [usize]
|
||||
},
|
||||
RunAborted,
|
||||
|
||||
WatchdogSetRequest { ms: u64 },
|
||||
WatchdogSetReply { id: usize },
|
||||
WatchdogClear { id: usize },
|
||||
|
||||
RpcSend {
|
||||
async: bool,
|
||||
service: u32,
|
||||
tag: &'a [u8],
|
||||
data: *const *const ()
|
||||
},
|
||||
RpcRecvRequest(*mut ()),
|
||||
RpcRecvReply(Result<usize, Exception<'a>>),
|
||||
|
||||
CacheGetRequest { key: &'a str },
|
||||
CacheGetReply { value: &'static [u32] },
|
||||
CachePutRequest { key: &'a str, value: &'static [u32] },
|
||||
CachePutReply { succeeded: bool },
|
||||
|
||||
Log(fmt::Arguments<'a>),
|
||||
LogSlice(&'a str)
|
||||
}
|
||||
|
||||
pub use self::Message::*;
|
@ -1,47 +1,144 @@
|
||||
#![no_std]
|
||||
#![feature(libc, const_fn, try_borrow, stmt_expr_attributes, repr_simd, asm,
|
||||
lang_items)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate std_artiq as std;
|
||||
extern crate libc;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate log_buffer;
|
||||
extern crate byteorder;
|
||||
extern crate fringe;
|
||||
extern crate lwip;
|
||||
|
||||
use std::prelude::v1::*;
|
||||
use core::fmt::Write;
|
||||
use logger::BufferLogger;
|
||||
|
||||
pub mod io;
|
||||
extern {
|
||||
fn putchar(c: libc::c_int) -> libc::c_int;
|
||||
fn readchar() -> libc::c_char;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => ($crate::print_fmt(format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
($fmt:expr) => (print!(concat!($fmt, "\n")));
|
||||
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
|
||||
}
|
||||
|
||||
pub struct Console;
|
||||
|
||||
impl core::fmt::Write for Console {
|
||||
fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
|
||||
for c in s.bytes() { unsafe { putchar(c as i32); } }
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_fmt(args: self::core::fmt::Arguments) {
|
||||
let _ = Console.write_fmt(args);
|
||||
}
|
||||
|
||||
#[lang = "panic_fmt"]
|
||||
extern fn panic_fmt(args: self::core::fmt::Arguments, file: &'static str, line: u32) -> ! {
|
||||
let _ = write!(Console, "panic at {}:{}: {}\n", file, line, args);
|
||||
let _ = write!(Console, "waiting for debugger...\n");
|
||||
unsafe {
|
||||
let _ = readchar();
|
||||
loop { asm!("l.trap 0") }
|
||||
}
|
||||
}
|
||||
|
||||
mod board;
|
||||
mod config;
|
||||
mod clock;
|
||||
mod rtio_crg;
|
||||
mod mailbox;
|
||||
mod rpc_queue;
|
||||
|
||||
mod urc;
|
||||
mod sched;
|
||||
mod logger;
|
||||
mod cache;
|
||||
|
||||
mod proto;
|
||||
mod kernel_proto;
|
||||
mod session_proto;
|
||||
mod moninj_proto;
|
||||
mod analyzer_proto;
|
||||
mod rpc_proto;
|
||||
|
||||
mod kernel;
|
||||
mod session;
|
||||
#[cfg(has_rtio_moninj)]
|
||||
mod moninj;
|
||||
#[cfg(has_rtio_analyzer)]
|
||||
mod analyzer;
|
||||
|
||||
extern {
|
||||
fn network_init();
|
||||
fn lwip_service();
|
||||
}
|
||||
|
||||
fn timer(waiter: io::Waiter) {
|
||||
loop {
|
||||
println!("tick");
|
||||
waiter.sleep(std::time::Duration::from_millis(1000)).unwrap();
|
||||
}
|
||||
}
|
||||
include!(concat!(env!("OUT_DIR"), "/git_info.rs"));
|
||||
|
||||
fn echo(waiter: io::Waiter) {
|
||||
let addr = io::SocketAddr::new(io::IP_ANY, 1234);
|
||||
let listener = io::TcpListener::bind(waiter, addr).unwrap();
|
||||
loop {
|
||||
let (mut stream, _addr) = listener.accept().unwrap();
|
||||
loop {
|
||||
let mut buf = [0];
|
||||
stream.read(&mut buf).unwrap();
|
||||
stream.write(&buf).unwrap();
|
||||
}
|
||||
}
|
||||
// Allow linking with crates that are built as -Cpanic=unwind even if we use -Cpanic=abort.
|
||||
// This is never called.
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn _Unwind_Resume() -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern fn rust_main() {
|
||||
println!("Accepting network sessions in Rust.");
|
||||
network_init();
|
||||
static mut LOG_BUFFER: [u8; 65536] = [0; 65536];
|
||||
BufferLogger::new(&mut LOG_BUFFER[..])
|
||||
.register(move || {
|
||||
info!("booting ARTIQ...");
|
||||
info!("software version {}", GIT_COMMIT);
|
||||
info!("gateware version {}", ::board::ident(&mut [0; 64]));
|
||||
|
||||
let mut scheduler = io::Scheduler::new();
|
||||
scheduler.spawn(4096, timer);
|
||||
scheduler.spawn(4096, echo);
|
||||
loop {
|
||||
lwip_service();
|
||||
scheduler.run()
|
||||
clock::init();
|
||||
rtio_crg::init();
|
||||
network_init();
|
||||
|
||||
let mut scheduler = sched::Scheduler::new();
|
||||
scheduler.spawner().spawn(16384, session::thread);
|
||||
#[cfg(has_rtio_moninj)]
|
||||
scheduler.spawner().spawn(4096, moninj::thread);
|
||||
#[cfg(has_rtio_analyzer)]
|
||||
scheduler.spawner().spawn(4096, analyzer::thread);
|
||||
|
||||
loop {
|
||||
scheduler.run();
|
||||
lwip_service();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern fn isr() {
|
||||
use board::{irq, csr};
|
||||
extern { fn uart_isr(); }
|
||||
|
||||
let irqs = irq::pending() & irq::get_mask();
|
||||
if irqs & (1 << csr::UART_INTERRUPT) != 0 {
|
||||
uart_isr()
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn sys_now() -> u32 {
|
||||
clock::get_ms() as u32
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn sys_jiffies() -> u32 {
|
||||
clock::get_ms() as u32
|
||||
}
|
||||
|
70
artiq/runtime.rs/src/logger.rs
Normal file
70
artiq/runtime.rs/src/logger.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use core::{mem, ptr};
|
||||
use core::cell::RefCell;
|
||||
use log::{self, Log, LogLevel, LogMetadata, LogRecord, LogLevelFilter};
|
||||
use log_buffer::LogBuffer;
|
||||
use clock;
|
||||
|
||||
pub struct BufferLogger {
|
||||
buffer: RefCell<LogBuffer<&'static mut [u8]>>
|
||||
}
|
||||
|
||||
unsafe impl Sync for BufferLogger {}
|
||||
|
||||
static mut LOGGER: *const BufferLogger = ptr::null();
|
||||
|
||||
impl BufferLogger {
|
||||
pub fn new(buffer: &'static mut [u8]) -> BufferLogger {
|
||||
BufferLogger {
|
||||
buffer: RefCell::new(LogBuffer::new(buffer))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register<F: FnOnce()>(&self, f: F) {
|
||||
// log::set_logger_raw captures a pointer to ourselves, so we must prevent
|
||||
// ourselves from being moved or dropped after that function is called (and
|
||||
// before log::shutdown_logger_raw is called).
|
||||
unsafe {
|
||||
log::set_logger_raw(|max_log_level| {
|
||||
max_log_level.set(LogLevelFilter::Trace);
|
||||
self as *const Log
|
||||
}).expect("global logger can only be initialized once");
|
||||
LOGGER = self;
|
||||
}
|
||||
f();
|
||||
log::shutdown_logger_raw().unwrap();
|
||||
unsafe {
|
||||
LOGGER = ptr::null();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_instance<R, F: FnOnce(&BufferLogger) -> R>(f: F) -> R {
|
||||
f(unsafe { mem::transmute::<*const BufferLogger, &BufferLogger>(LOGGER) })
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.buffer.borrow_mut().clear()
|
||||
}
|
||||
|
||||
pub fn extract<R, F: FnOnce(&str) -> R>(&self, f: F) -> R {
|
||||
f(self.buffer.borrow_mut().extract())
|
||||
}
|
||||
}
|
||||
|
||||
impl Log for BufferLogger {
|
||||
fn enabled(&self, _metadata: &LogMetadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn log(&self, record: &LogRecord) {
|
||||
if self.enabled(record.metadata()) {
|
||||
use core::fmt::Write;
|
||||
writeln!(self.buffer.borrow_mut(),
|
||||
"[{:12}us] {:>5}({}): {}",
|
||||
clock::get_us(), record.level(), record.target(), record.args()).unwrap();
|
||||
if record.level() <= LogLevel::Info {
|
||||
println!("[{:12}us] {:>5}({}): {}",
|
||||
clock::get_us(), record.level(), record.target(), record.args());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
35
artiq/runtime.rs/src/mailbox.rs
Normal file
35
artiq/runtime.rs/src/mailbox.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use core::ptr::{read_volatile, write_volatile};
|
||||
use board;
|
||||
|
||||
const MAILBOX: *mut usize = board::mem::MAILBOX_BASE as *mut usize;
|
||||
static mut last: usize = 0;
|
||||
|
||||
pub unsafe fn send(data: usize) {
|
||||
last = data;
|
||||
write_volatile(MAILBOX, data)
|
||||
}
|
||||
|
||||
pub fn acknowledged() -> bool {
|
||||
unsafe {
|
||||
let data = read_volatile(MAILBOX);
|
||||
data == 0 || data != last
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive() -> usize {
|
||||
unsafe {
|
||||
let data = read_volatile(MAILBOX);
|
||||
if data == last {
|
||||
0
|
||||
} else {
|
||||
if data != 0 {
|
||||
board::flush_cpu_dcache()
|
||||
}
|
||||
data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn acknowledge() {
|
||||
unsafe { write_volatile(MAILBOX, 0) }
|
||||
}
|
124
artiq/runtime.rs/src/moninj.rs
Normal file
124
artiq/runtime.rs/src/moninj.rs
Normal file
@ -0,0 +1,124 @@
|
||||
use std::vec::Vec;
|
||||
use std::io;
|
||||
use board::csr;
|
||||
use sched::{Waiter, Spawner};
|
||||
use sched::{UdpSocket, SocketAddr, IP_ANY};
|
||||
use moninj_proto::*;
|
||||
|
||||
const MONINJ_TTL_OVERRIDE_ENABLE: u8 = 0;
|
||||
const MONINJ_TTL_OVERRIDE_O: u8 = 1;
|
||||
const MONINJ_TTL_OVERRIDE_OE: u8 = 2;
|
||||
|
||||
fn worker(socket: &mut UdpSocket) -> io::Result<()> {
|
||||
let mut buf = Vec::new();
|
||||
loop {
|
||||
let addr = try!(socket.recv_from(&mut buf));
|
||||
let request = try!(Request::read_from(&mut io::Cursor::new(&buf)));
|
||||
trace!("{} -> {:?}", addr, request);
|
||||
|
||||
match request {
|
||||
Request::Monitor => {
|
||||
let mut dds_ftws = [0u32; (csr::CONFIG_RTIO_DDS_COUNT as usize *
|
||||
csr::CONFIG_DDS_CHANNELS_PER_BUS as usize)];
|
||||
let mut reply = Reply::default();
|
||||
|
||||
for i in 0..csr::CONFIG_RTIO_REGULAR_TTL_COUNT as u8 {
|
||||
unsafe {
|
||||
csr::rtio_moninj::mon_chan_sel_write(i);
|
||||
csr::rtio_moninj::mon_probe_sel_write(0);
|
||||
csr::rtio_moninj::mon_value_update_write(1);
|
||||
if csr::rtio_moninj::mon_value_read() != 0 {
|
||||
reply.ttl_levels |= 1 << i;
|
||||
}
|
||||
csr::rtio_moninj::mon_probe_sel_write(1);
|
||||
csr::rtio_moninj::mon_value_update_write(1);
|
||||
if csr::rtio_moninj::mon_value_read() != 0 {
|
||||
reply.ttl_oes |= 1 << i;
|
||||
}
|
||||
csr::rtio_moninj::inj_chan_sel_write(i);
|
||||
csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_ENABLE);
|
||||
if csr::rtio_moninj::inj_value_read() != 0 {
|
||||
reply.ttl_overrides |= 1 << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reply.dds_rtio_first_channel = csr::CONFIG_RTIO_FIRST_DDS_CHANNEL as u16;
|
||||
reply.dds_channels_per_bus = csr::CONFIG_DDS_CHANNELS_PER_BUS as u16;
|
||||
|
||||
for j in 0..csr::CONFIG_RTIO_DDS_COUNT {
|
||||
unsafe {
|
||||
csr::rtio_moninj::mon_chan_sel_write(
|
||||
(csr::CONFIG_RTIO_FIRST_DDS_CHANNEL + j) as u8);
|
||||
for i in 0..csr::CONFIG_DDS_CHANNELS_PER_BUS {
|
||||
csr::rtio_moninj::mon_probe_sel_write(i as u8);
|
||||
csr::rtio_moninj::mon_value_update_write(1);
|
||||
dds_ftws[(csr::CONFIG_DDS_CHANNELS_PER_BUS * j + i) as usize] =
|
||||
csr::rtio_moninj::mon_value_read() as u32;
|
||||
}
|
||||
}
|
||||
}
|
||||
reply.dds_ftws = &dds_ftws;
|
||||
|
||||
trace!("{} <- {:?}", addr, reply);
|
||||
buf.clear();
|
||||
try!(reply.write_to(&mut buf));
|
||||
try!(socket.send_to(&buf, addr));
|
||||
},
|
||||
|
||||
Request::TtlSet { channel, mode: TtlMode::Experiment } => {
|
||||
unsafe {
|
||||
csr::rtio_moninj::inj_chan_sel_write(channel);
|
||||
csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_ENABLE);
|
||||
csr::rtio_moninj::inj_value_write(0);
|
||||
}
|
||||
},
|
||||
|
||||
Request::TtlSet { channel, mode: TtlMode::High } => {
|
||||
unsafe {
|
||||
csr::rtio_moninj::inj_chan_sel_write(channel);
|
||||
csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_O);
|
||||
csr::rtio_moninj::inj_value_write(1);
|
||||
csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_OE);
|
||||
csr::rtio_moninj::inj_value_write(1);
|
||||
csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_ENABLE);
|
||||
csr::rtio_moninj::inj_value_write(1);
|
||||
}
|
||||
},
|
||||
|
||||
Request::TtlSet { channel, mode: TtlMode::Low } => {
|
||||
unsafe {
|
||||
csr::rtio_moninj::inj_chan_sel_write(channel);
|
||||
csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_O);
|
||||
csr::rtio_moninj::inj_value_write(0);
|
||||
csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_OE);
|
||||
csr::rtio_moninj::inj_value_write(1);
|
||||
csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_ENABLE);
|
||||
csr::rtio_moninj::inj_value_write(1);
|
||||
}
|
||||
},
|
||||
|
||||
Request::TtlSet { channel, mode: TtlMode::Input } => {
|
||||
unsafe {
|
||||
csr::rtio_moninj::inj_chan_sel_write(channel);
|
||||
csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_OE);
|
||||
csr::rtio_moninj::inj_value_write(0);
|
||||
csr::rtio_moninj::inj_override_sel_write(MONINJ_TTL_OVERRIDE_ENABLE);
|
||||
csr::rtio_moninj::inj_value_write(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn thread(waiter: Waiter, _spawner: Spawner) {
|
||||
let mut socket = UdpSocket::new(waiter).expect("cannot create socket");
|
||||
socket.bind(SocketAddr::new(IP_ANY, 3250)).expect("cannot bind socket");
|
||||
|
||||
loop {
|
||||
match worker(&mut socket) {
|
||||
Ok(()) => unreachable!(),
|
||||
Err(err) => error!("moninj aborted: {}", err)
|
||||
}
|
||||
}
|
||||
}
|
65
artiq/runtime.rs/src/moninj_proto.rs
Normal file
65
artiq/runtime.rs/src/moninj_proto.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use std::io::{self, Read, Write};
|
||||
use proto::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TtlMode {
|
||||
Experiment,
|
||||
High,
|
||||
Low,
|
||||
Input
|
||||
}
|
||||
|
||||
impl TtlMode {
|
||||
pub fn read_from(reader: &mut Read) -> io::Result<TtlMode> {
|
||||
Ok(match try!(read_u8(reader)) {
|
||||
0 => TtlMode::Experiment,
|
||||
1 => TtlMode::High,
|
||||
2 => TtlMode::Low,
|
||||
3 => TtlMode::Input,
|
||||
_ => return Err(io::Error::new(io::ErrorKind::InvalidData, "unknown TTL mode"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Request {
|
||||
Monitor,
|
||||
TtlSet { channel: u8, mode: TtlMode }
|
||||
}
|
||||
|
||||
impl Request {
|
||||
pub fn read_from(reader: &mut Read) -> io::Result<Request> {
|
||||
Ok(match try!(read_u8(reader)) {
|
||||
1 => Request::Monitor,
|
||||
2 => Request::TtlSet {
|
||||
channel: try!(read_u8(reader)),
|
||||
mode: try!(TtlMode::read_from(reader))
|
||||
},
|
||||
_ => return Err(io::Error::new(io::ErrorKind::InvalidData, "unknown request type"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Reply<'a> {
|
||||
pub ttl_levels: u64,
|
||||
pub ttl_oes: u64,
|
||||
pub ttl_overrides: u64,
|
||||
pub dds_rtio_first_channel: u16,
|
||||
pub dds_channels_per_bus: u16,
|
||||
pub dds_ftws: &'a [u32]
|
||||
}
|
||||
|
||||
impl<'a> Reply<'a> {
|
||||
pub fn write_to(&self, writer: &mut Write) -> io::Result<()> {
|
||||
try!(write_u64(writer, self.ttl_levels));
|
||||
try!(write_u64(writer, self.ttl_oes));
|
||||
try!(write_u64(writer, self.ttl_overrides));
|
||||
try!(write_u16(writer, self.dds_rtio_first_channel));
|
||||
try!(write_u16(writer, self.dds_channels_per_bus));
|
||||
for dds_ftw in self.dds_ftws {
|
||||
try!(write_u32(writer, *dds_ftw));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
76
artiq/runtime.rs/src/proto.rs
Normal file
76
artiq/runtime.rs/src/proto.rs
Normal file
@ -0,0 +1,76 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::vec::Vec;
|
||||
use std::string::String;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
|
||||
// FIXME: replace these with byteorder core io traits once those are in
|
||||
pub fn read_u8(reader: &mut Read) -> io::Result<u8> {
|
||||
let mut bytes = [0; 1];
|
||||
try!(reader.read_exact(&mut bytes));
|
||||
Ok(bytes[0])
|
||||
}
|
||||
|
||||
pub fn write_u8(writer: &mut Write, value: u8) -> io::Result<()> {
|
||||
let bytes = [value; 1];
|
||||
writer.write_all(&bytes)
|
||||
}
|
||||
|
||||
pub fn read_u16(reader: &mut Read) -> io::Result<u16> {
|
||||
let mut bytes = [0; 2];
|
||||
try!(reader.read_exact(&mut bytes));
|
||||
Ok(NetworkEndian::read_u16(&bytes))
|
||||
}
|
||||
|
||||
pub fn write_u16(writer: &mut Write, value: u16) -> io::Result<()> {
|
||||
let mut bytes = [0; 2];
|
||||
NetworkEndian::write_u16(&mut bytes, value);
|
||||
writer.write_all(&bytes)
|
||||
}
|
||||
|
||||
pub fn read_u32(reader: &mut Read) -> io::Result<u32> {
|
||||
let mut bytes = [0; 4];
|
||||
try!(reader.read_exact(&mut bytes));
|
||||
Ok(NetworkEndian::read_u32(&bytes))
|
||||
}
|
||||
|
||||
pub fn write_u32(writer: &mut Write, value: u32) -> io::Result<()> {
|
||||
let mut bytes = [0; 4];
|
||||
NetworkEndian::write_u32(&mut bytes, value);
|
||||
writer.write_all(&bytes)
|
||||
}
|
||||
|
||||
pub fn read_u64(reader: &mut Read) -> io::Result<u64> {
|
||||
let mut bytes = [0; 8];
|
||||
try!(reader.read_exact(&mut bytes));
|
||||
Ok(NetworkEndian::read_u64(&bytes))
|
||||
}
|
||||
|
||||
pub fn write_u64(writer: &mut Write, value: u64) -> io::Result<()> {
|
||||
let mut bytes = [0; 8];
|
||||
NetworkEndian::write_u64(&mut bytes, value);
|
||||
writer.write_all(&bytes)
|
||||
}
|
||||
|
||||
pub fn read_bytes(reader: &mut Read) -> io::Result<Vec<u8>> {
|
||||
let length = try!(read_u32(reader));
|
||||
let mut value = vec![0; length as usize];
|
||||
try!(reader.read_exact(&mut value));
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
pub fn write_bytes(writer: &mut Write, value: &[u8]) -> io::Result<()> {
|
||||
try!(write_u32(writer, value.len() as u32));
|
||||
writer.write_all(value)
|
||||
}
|
||||
|
||||
pub fn read_string(reader: &mut Read) -> io::Result<String> {
|
||||
let bytes = try!(read_bytes(reader));
|
||||
String::from_utf8(bytes)
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid UTF-8"))
|
||||
}
|
||||
|
||||
pub fn write_string(writer: &mut Write, value: &str) -> io::Result<()> {
|
||||
write_bytes(writer, value.as_bytes())
|
||||
}
|
369
artiq/runtime.rs/src/rpc_proto.rs
Normal file
369
artiq/runtime.rs/src/rpc_proto.rs
Normal file
@ -0,0 +1,369 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use core::slice;
|
||||
use std::io::{self, Read, Write};
|
||||
use proto::*;
|
||||
use self::tag::{Tag, TagIterator, split_tag};
|
||||
|
||||
unsafe fn recv_value(reader: &mut Read, tag: Tag, data: &mut *mut (),
|
||||
alloc: &Fn(usize) -> io::Result<*mut ()>) -> io::Result<()> {
|
||||
macro_rules! consume_value {
|
||||
($ty:ty, |$ptr:ident| $map:expr) => ({
|
||||
let ptr = (*data) as *mut $ty;
|
||||
*data = ptr.offset(1) as *mut ();
|
||||
(|$ptr: *mut $ty| $map)(ptr)
|
||||
})
|
||||
}
|
||||
|
||||
match tag {
|
||||
Tag::None => Ok(()),
|
||||
Tag::Bool =>
|
||||
consume_value!(u8, |ptr| {
|
||||
*ptr = try!(read_u8(reader)); Ok(())
|
||||
}),
|
||||
Tag::Int32 =>
|
||||
consume_value!(u32, |ptr| {
|
||||
*ptr = try!(read_u32(reader)); Ok(())
|
||||
}),
|
||||
Tag::Int64 | Tag::Float64 =>
|
||||
consume_value!(u64, |ptr| {
|
||||
*ptr = try!(read_u64(reader)); Ok(())
|
||||
}),
|
||||
Tag::String => {
|
||||
consume_value!(*mut u8, |ptr| {
|
||||
let length = try!(read_u32(reader));
|
||||
// NB: the received string includes a trailing \0
|
||||
*ptr = try!(alloc(length as usize)) as *mut u8;
|
||||
try!(reader.read_exact(slice::from_raw_parts_mut(*ptr, length as usize)));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
Tag::Tuple(it, arity) => {
|
||||
let mut it = it.clone();
|
||||
for _ in 0..arity {
|
||||
let tag = it.next().expect("truncated tag");
|
||||
try!(recv_value(reader, tag, data, alloc))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Tag::List(it) | Tag::Array(it) => {
|
||||
struct List { length: u32, elements: *mut () };
|
||||
consume_value!(List, |ptr| {
|
||||
(*ptr).length = try!(read_u32(reader));
|
||||
|
||||
let tag = it.clone().next().expect("truncated tag");
|
||||
(*ptr).elements = try!(alloc(tag.size() * (*ptr).length as usize));
|
||||
|
||||
let mut data = (*ptr).elements;
|
||||
for _ in 0..(*ptr).length as usize {
|
||||
try!(recv_value(reader, tag, &mut data, alloc));
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
Tag::Range(it) => {
|
||||
let tag = it.clone().next().expect("truncated tag");
|
||||
try!(recv_value(reader, tag, data, alloc));
|
||||
try!(recv_value(reader, tag, data, alloc));
|
||||
try!(recv_value(reader, tag, data, alloc));
|
||||
Ok(())
|
||||
}
|
||||
Tag::Keyword(_) => unreachable!(),
|
||||
Tag::Object => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recv_return(reader: &mut Read, tag_bytes: &[u8], data: *mut (),
|
||||
alloc: &Fn(usize) -> io::Result<*mut ()>) -> io::Result<()> {
|
||||
let mut it = TagIterator::new(tag_bytes);
|
||||
#[cfg(not(ksupport))]
|
||||
trace!("recv ...->{}", it);
|
||||
|
||||
let tag = it.next().expect("truncated tag");
|
||||
let mut data = data;
|
||||
try!(unsafe { recv_value(reader, tag, &mut data, alloc) });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub unsafe fn from_c_str<'a>(ptr: *const u8) -> &'a str {
|
||||
use core::{str, slice};
|
||||
extern { fn strlen(ptr: *const u8) -> usize; }
|
||||
str::from_utf8_unchecked(slice::from_raw_parts(ptr as *const u8, strlen(ptr)))
|
||||
}
|
||||
|
||||
unsafe fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io::Result<()> {
|
||||
macro_rules! consume_value {
|
||||
($ty:ty, |$ptr:ident| $map:expr) => ({
|
||||
let ptr = (*data) as *const $ty;
|
||||
*data = ptr.offset(1) as *const ();
|
||||
(|$ptr: *const $ty| $map)(ptr)
|
||||
})
|
||||
}
|
||||
|
||||
try!(write_u8(writer, tag.as_u8()));
|
||||
match tag {
|
||||
Tag::None => Ok(()),
|
||||
Tag::Bool =>
|
||||
consume_value!(u8, |ptr|
|
||||
write_u8(writer, *ptr)),
|
||||
Tag::Int32 =>
|
||||
consume_value!(u32, |ptr|
|
||||
write_u32(writer, *ptr)),
|
||||
Tag::Int64 | Tag::Float64 =>
|
||||
consume_value!(u64, |ptr|
|
||||
write_u64(writer, *ptr)),
|
||||
Tag::String =>
|
||||
consume_value!(*const u8, |ptr|
|
||||
write_string(writer, from_c_str(*ptr))),
|
||||
Tag::Tuple(it, arity) => {
|
||||
let mut it = it.clone();
|
||||
try!(write_u8(writer, arity));
|
||||
for _ in 0..arity {
|
||||
let tag = it.next().expect("truncated tag");
|
||||
try!(send_value(writer, tag, data))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Tag::List(it) | Tag::Array(it) => {
|
||||
struct List { length: u32, elements: *const () };
|
||||
consume_value!(List, |ptr| {
|
||||
try!(write_u32(writer, (*ptr).length));
|
||||
let tag = it.clone().next().expect("truncated tag");
|
||||
let mut data = (*ptr).elements;
|
||||
for _ in 0..(*ptr).length as usize {
|
||||
try!(send_value(writer, tag, &mut data));
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
Tag::Range(it) => {
|
||||
let tag = it.clone().next().expect("truncated tag");
|
||||
try!(send_value(writer, tag, data));
|
||||
try!(send_value(writer, tag, data));
|
||||
try!(send_value(writer, tag, data));
|
||||
Ok(())
|
||||
}
|
||||
Tag::Keyword(it) => {
|
||||
struct Keyword { name: *const u8, contents: () };
|
||||
consume_value!(Keyword, |ptr| {
|
||||
try!(write_string(writer, from_c_str((*ptr).name)));
|
||||
let tag = it.clone().next().expect("truncated tag");
|
||||
let mut data = &(*ptr).contents as *const ();
|
||||
send_value(writer, tag, &mut data)
|
||||
})
|
||||
// Tag::Keyword never appears in composite types, so we don't have
|
||||
// to accurately advance data.
|
||||
}
|
||||
Tag::Object => {
|
||||
struct Object { id: u32 };
|
||||
consume_value!(*const Object, |ptr|
|
||||
write_u32(writer, (**ptr).id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_args(writer: &mut Write, service: u32, tag_bytes: &[u8],
|
||||
data: *const *const ()) -> io::Result<()> {
|
||||
let (arg_tags_bytes, return_tag_bytes) = split_tag(tag_bytes);
|
||||
|
||||
let mut args_it = TagIterator::new(arg_tags_bytes);
|
||||
let return_it = TagIterator::new(return_tag_bytes);
|
||||
#[cfg(not(ksupport))]
|
||||
trace!("send<{}>({})->{}", service, args_it, return_it);
|
||||
|
||||
try!(write_u32(writer, service));
|
||||
for index in 0.. {
|
||||
if let Some(arg_tag) = args_it.next() {
|
||||
let mut data = unsafe { *data.offset(index) };
|
||||
try!(unsafe { send_value(writer, arg_tag, &mut data) });
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
try!(write_u8(writer, 0));
|
||||
try!(write_bytes(writer, return_tag_bytes));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
mod tag {
|
||||
use core::fmt;
|
||||
|
||||
pub fn split_tag(tag_bytes: &[u8]) -> (&[u8], &[u8]) {
|
||||
let tag_separator =
|
||||
tag_bytes.iter()
|
||||
.position(|&b| b == b':')
|
||||
.expect("tag without a return separator");
|
||||
let (arg_tags_bytes, rest) = tag_bytes.split_at(tag_separator);
|
||||
let return_tag_bytes = &rest[1..];
|
||||
(arg_tags_bytes, return_tag_bytes)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Tag<'a> {
|
||||
None,
|
||||
Bool,
|
||||
Int32,
|
||||
Int64,
|
||||
Float64,
|
||||
String,
|
||||
Tuple(TagIterator<'a>, u8),
|
||||
List(TagIterator<'a>),
|
||||
Array(TagIterator<'a>),
|
||||
Range(TagIterator<'a>),
|
||||
Keyword(TagIterator<'a>),
|
||||
Object
|
||||
}
|
||||
|
||||
impl<'a> Tag<'a> {
|
||||
pub fn as_u8(self) -> u8 {
|
||||
match self {
|
||||
Tag::None => b'n',
|
||||
Tag::Bool => b'b',
|
||||
Tag::Int32 => b'i',
|
||||
Tag::Int64 => b'I',
|
||||
Tag::Float64 => b'f',
|
||||
Tag::String => b's',
|
||||
Tag::Tuple(_, _) => b't',
|
||||
Tag::List(_) => b'l',
|
||||
Tag::Array(_) => b'a',
|
||||
Tag::Range(_) => b'r',
|
||||
Tag::Keyword(_) => b'k',
|
||||
Tag::Object => b'O',
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(self) -> usize {
|
||||
match self {
|
||||
Tag::None => 0,
|
||||
Tag::Bool => 1,
|
||||
Tag::Int32 => 4,
|
||||
Tag::Int64 => 8,
|
||||
Tag::Float64 => 8,
|
||||
Tag::String => 4,
|
||||
Tag::Tuple(it, arity) => {
|
||||
let mut size = 0;
|
||||
for _ in 0..arity {
|
||||
let tag = it.clone().next().expect("truncated tag");
|
||||
size += tag.size();
|
||||
}
|
||||
size
|
||||
}
|
||||
Tag::List(_) => 8,
|
||||
Tag::Array(_) => 8,
|
||||
Tag::Range(it) => {
|
||||
let tag = it.clone().next().expect("truncated tag");
|
||||
tag.size() * 3
|
||||
}
|
||||
Tag::Keyword(_) => unreachable!(),
|
||||
Tag::Object => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct TagIterator<'a> {
|
||||
data: &'a [u8]
|
||||
}
|
||||
|
||||
impl<'a> TagIterator<'a> {
|
||||
pub fn new(data: &'a [u8]) -> TagIterator<'a> {
|
||||
TagIterator { data: data }
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> Option<Tag<'a>> {
|
||||
if self.data.len() == 0 {
|
||||
return None
|
||||
}
|
||||
|
||||
let tag_byte = self.data[0];
|
||||
self.data = &self.data[1..];
|
||||
Some(match tag_byte {
|
||||
b'n' => Tag::None,
|
||||
b'b' => Tag::Bool,
|
||||
b'i' => Tag::Int32,
|
||||
b'I' => Tag::Int64,
|
||||
b'f' => Tag::Float64,
|
||||
b's' => Tag::String,
|
||||
b't' => {
|
||||
let count = self.data[0];
|
||||
self.data = &self.data[1..];
|
||||
Tag::Tuple(self.sub(count), count)
|
||||
}
|
||||
b'l' => Tag::List(self.sub(1)),
|
||||
b'a' => Tag::Array(self.sub(1)),
|
||||
b'r' => Tag::Range(self.sub(1)),
|
||||
b'k' => Tag::Keyword(self.sub(1)),
|
||||
b'O' => Tag::Object,
|
||||
_ => unreachable!()
|
||||
})
|
||||
}
|
||||
|
||||
fn sub(&mut self, count: u8) -> TagIterator<'a> {
|
||||
let data = self.data;
|
||||
for _ in 0..count {
|
||||
self.next().expect("truncated tag");
|
||||
}
|
||||
TagIterator { data: &data[..(data.len() - self.data.len())] }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for TagIterator<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut it = self.clone();
|
||||
let mut first = true;
|
||||
while let Some(tag) = it.next() {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
try!(write!(f, ", "))
|
||||
}
|
||||
|
||||
match tag {
|
||||
Tag::None =>
|
||||
try!(write!(f, "None")),
|
||||
Tag::Bool =>
|
||||
try!(write!(f, "Bool")),
|
||||
Tag::Int32 =>
|
||||
try!(write!(f, "Int32")),
|
||||
Tag::Int64 =>
|
||||
try!(write!(f, "Int64")),
|
||||
Tag::Float64 =>
|
||||
try!(write!(f, "Float64")),
|
||||
Tag::String =>
|
||||
try!(write!(f, "String")),
|
||||
Tag::Tuple(it, _) => {
|
||||
try!(write!(f, "Tuple("));
|
||||
try!(it.fmt(f));
|
||||
try!(write!(f, ")"))
|
||||
}
|
||||
Tag::List(it) => {
|
||||
try!(write!(f, "List("));
|
||||
try!(it.fmt(f));
|
||||
try!(write!(f, ")"))
|
||||
}
|
||||
Tag::Array(it) => {
|
||||
try!(write!(f, "Array("));
|
||||
try!(it.fmt(f));
|
||||
try!(write!(f, ")"))
|
||||
}
|
||||
Tag::Range(it) => {
|
||||
try!(write!(f, "Range("));
|
||||
try!(it.fmt(f));
|
||||
try!(write!(f, ")"))
|
||||
}
|
||||
Tag::Keyword(it) => {
|
||||
try!(write!(f, "Keyword("));
|
||||
try!(it.fmt(f));
|
||||
try!(write!(f, ")"))
|
||||
}
|
||||
Tag::Object =>
|
||||
try!(write!(f, "Object"))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
61
artiq/runtime.rs/src/rpc_queue.rs
Normal file
61
artiq/runtime.rs/src/rpc_queue.rs
Normal file
@ -0,0 +1,61 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use core::ptr::{read_volatile, write_volatile};
|
||||
use core::slice;
|
||||
use board;
|
||||
|
||||
const SEND_MAILBOX: *mut usize = (board::mem::MAILBOX_BASE + 4) as *mut usize;
|
||||
const RECV_MAILBOX: *mut usize = (board::mem::MAILBOX_BASE + 8) as *mut usize;
|
||||
|
||||
const QUEUE_BEGIN: usize = 0x40400000;
|
||||
const QUEUE_END: usize = 0x407fff80;
|
||||
const QUEUE_CHUNK: usize = 0x1000;
|
||||
|
||||
pub unsafe fn init() {
|
||||
write_volatile(SEND_MAILBOX, QUEUE_BEGIN);
|
||||
write_volatile(RECV_MAILBOX, QUEUE_BEGIN);
|
||||
}
|
||||
|
||||
fn next(mut addr: usize) -> usize {
|
||||
debug_assert!(addr % QUEUE_CHUNK == 0);
|
||||
debug_assert!(addr >= QUEUE_BEGIN && addr < QUEUE_END);
|
||||
|
||||
addr += QUEUE_CHUNK;
|
||||
if addr >= QUEUE_END { addr = QUEUE_BEGIN }
|
||||
addr
|
||||
}
|
||||
|
||||
pub fn empty() -> bool {
|
||||
unsafe { read_volatile(SEND_MAILBOX) == read_volatile(RECV_MAILBOX) }
|
||||
}
|
||||
|
||||
pub fn full() -> bool {
|
||||
unsafe { next(read_volatile(SEND_MAILBOX)) == read_volatile(RECV_MAILBOX) }
|
||||
}
|
||||
|
||||
pub fn enqueue<T, E, F>(f: F) -> Result<T, E>
|
||||
where F: FnOnce(&mut [u8]) -> Result<T, E> {
|
||||
debug_assert!(!full());
|
||||
|
||||
unsafe {
|
||||
let slice = slice::from_raw_parts_mut(read_volatile(SEND_MAILBOX) as *mut u8, QUEUE_CHUNK);
|
||||
f(slice).and_then(|x| {
|
||||
write_volatile(SEND_MAILBOX, next(read_volatile(SEND_MAILBOX)));
|
||||
Ok(x)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dequeue<T, E, F>(f: F) -> Result<T, E>
|
||||
where F: FnOnce(&mut [u8]) -> Result<T, E> {
|
||||
debug_assert!(!empty());
|
||||
|
||||
unsafe {
|
||||
board::flush_cpu_dcache();
|
||||
let slice = slice::from_raw_parts_mut(read_volatile(RECV_MAILBOX) as *mut u8, QUEUE_CHUNK);
|
||||
f(slice).and_then(|x| {
|
||||
write_volatile(RECV_MAILBOX, next(read_volatile(RECV_MAILBOX)));
|
||||
Ok(x)
|
||||
})
|
||||
}
|
||||
}
|
65
artiq/runtime.rs/src/rtio_crg.rs
Normal file
65
artiq/runtime.rs/src/rtio_crg.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use config;
|
||||
|
||||
#[cfg(has_rtio_crg)]
|
||||
mod imp {
|
||||
use board::csr;
|
||||
use clock;
|
||||
|
||||
pub fn init() {
|
||||
unsafe { csr::rtio_crg::pll_reset_write(0) }
|
||||
}
|
||||
|
||||
pub fn check() -> bool {
|
||||
unsafe { csr::rtio_crg::pll_locked_read() != 0 }
|
||||
}
|
||||
|
||||
pub fn switch_clock(clk: u8) -> bool {
|
||||
unsafe {
|
||||
let cur_clk = csr::rtio_crg::clock_sel_read();
|
||||
if clk != cur_clk {
|
||||
csr::rtio_crg::pll_reset_write(1);
|
||||
csr::rtio_crg::clock_sel_write(clk);
|
||||
csr::rtio_crg::pll_reset_write(0);
|
||||
}
|
||||
}
|
||||
|
||||
clock::spin_us(150);
|
||||
return check()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(has_rtio_crg))]
|
||||
mod imp {
|
||||
pub fn init() {}
|
||||
pub fn check() -> bool { true }
|
||||
pub fn switch_clock(clk: u8) -> bool { true }
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
imp::init();
|
||||
|
||||
let mut opt = [b'i'];
|
||||
let clk;
|
||||
match config::read("startup_clock", &mut opt) {
|
||||
Ok(0) | Ok(1) if &opt == b"i" => {
|
||||
info!("startup RTIO clock: internal");
|
||||
clk = 0
|
||||
}
|
||||
Ok(1) if &opt == b"e" => {
|
||||
info!("startup RTIO clock: external");
|
||||
clk = 1
|
||||
}
|
||||
_ => {
|
||||
error!("unrecognized startup_clock configuration entry");
|
||||
clk = 0
|
||||
}
|
||||
};
|
||||
|
||||
if !switch_clock(clk) {
|
||||
error!("startup RTIO clock failed");
|
||||
warn!("this may cause the system initialization to fail");
|
||||
warn!("fix clocking and reset the device");
|
||||
}
|
||||
}
|
||||
|
||||
pub use self::imp::{check, switch_clock};
|
@ -1,16 +1,17 @@
|
||||
extern crate fringe;
|
||||
extern crate lwip;
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::vec::Vec;
|
||||
use std::time::{Instant, Duration};
|
||||
use std::io::{Read, Write, Result, Error, ErrorKind};
|
||||
use self::fringe::OwnedStack;
|
||||
use self::fringe::generator::{Generator, Yielder};
|
||||
use fringe::OwnedStack;
|
||||
use fringe::generator::{Generator, Yielder, State as GeneratorState};
|
||||
use lwip;
|
||||
use clock;
|
||||
use urc::Urc;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct WaitRequest {
|
||||
timeout: Option<Instant>,
|
||||
timeout: Option<u64>,
|
||||
event: Option<WaitEvent>
|
||||
}
|
||||
|
||||
@ -23,48 +24,96 @@ enum WaitResult {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Thread {
|
||||
generator: Generator<WaitResult, WaitRequest, OwnedStack>,
|
||||
generator: Generator<WaitResult, WaitRequest, OwnedStack>,
|
||||
waiting_for: WaitRequest,
|
||||
interrupted: bool
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Scheduler {
|
||||
threads: Vec<Thread>,
|
||||
index: usize
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
pub fn new() -> Scheduler {
|
||||
Scheduler { threads: Vec::new(), index: 0 }
|
||||
}
|
||||
|
||||
pub unsafe fn spawn<F: FnOnce(Waiter) + Send + 'static>(&mut self, stack_size: usize, f: F) {
|
||||
impl Thread {
|
||||
unsafe fn new<F>(spawner: Spawner, stack_size: usize, f: F) -> ThreadHandle
|
||||
where F: 'static + FnOnce(Waiter, Spawner) + Send {
|
||||
let stack = OwnedStack::new(stack_size);
|
||||
let thread = Thread {
|
||||
generator: Generator::unsafe_new(stack, move |yielder, _| {
|
||||
f(Waiter(yielder))
|
||||
ThreadHandle::new(Thread {
|
||||
generator: Generator::unsafe_new(stack, |yielder, _| {
|
||||
f(Waiter(yielder), spawner)
|
||||
}),
|
||||
waiting_for: WaitRequest {
|
||||
timeout: None,
|
||||
event: None
|
||||
},
|
||||
interrupted: false
|
||||
};
|
||||
self.threads.push(thread)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn terminated(&self) -> bool {
|
||||
// FIXME: https://github.com/nathan7/libfringe/pull/56
|
||||
match self.generator.state() {
|
||||
GeneratorState::Unavailable => true,
|
||||
GeneratorState::Runnable => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interrupt(&mut self) {
|
||||
self.interrupted = true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ThreadHandle(Urc<RefCell<Thread>>);
|
||||
|
||||
impl ThreadHandle {
|
||||
fn new(thread: Thread) -> ThreadHandle {
|
||||
ThreadHandle(Urc::new(RefCell::new(thread)))
|
||||
}
|
||||
|
||||
pub fn terminated(&self) -> bool {
|
||||
match self.0.try_borrow() {
|
||||
Ok(thread) => thread.terminated(),
|
||||
Err(_) => false // the running thread hasn't terminated
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interrupt(&self) {
|
||||
match self.0.try_borrow_mut() {
|
||||
Ok(mut thread) => thread.interrupt(),
|
||||
Err(_) => panic!("cannot interrupt the running thread")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Scheduler {
|
||||
threads: Vec<ThreadHandle>,
|
||||
index: usize,
|
||||
spawner: Spawner
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
pub fn new() -> Scheduler {
|
||||
Scheduler {
|
||||
threads: Vec::new(),
|
||||
index: 0,
|
||||
spawner: Spawner::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawner(&self) -> &Spawner {
|
||||
&self.spawner
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
self.threads.append(&mut *self.spawner.queue.borrow_mut());
|
||||
|
||||
if self.threads.len() == 0 { return }
|
||||
|
||||
let now = Instant::now();
|
||||
let now = clock::get_ms();
|
||||
|
||||
let start_index = self.index;
|
||||
loop {
|
||||
self.index = (self.index + 1) % self.threads.len();
|
||||
|
||||
let result = {
|
||||
let thread = &mut self.threads[self.index];
|
||||
let thread = &mut *self.threads[self.index].0.borrow_mut();
|
||||
match thread.waiting_for {
|
||||
_ if thread.interrupted => {
|
||||
thread.interrupted = false;
|
||||
@ -95,7 +144,8 @@ impl Scheduler {
|
||||
},
|
||||
Some(wait_request) => {
|
||||
// The thread has suspended itself.
|
||||
self.threads[self.index].waiting_for = wait_request
|
||||
let thread = &mut *self.threads[self.index].0.borrow_mut();
|
||||
thread.waiting_for = wait_request
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,8 +154,27 @@ impl Scheduler {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Spawner {
|
||||
queue: Urc<RefCell<Vec<ThreadHandle>>>
|
||||
}
|
||||
|
||||
impl Spawner {
|
||||
fn new() -> Spawner {
|
||||
Spawner { queue: Urc::new(RefCell::new(Vec::new())) }
|
||||
}
|
||||
|
||||
pub fn spawn<F>(&self, stack_size: usize, f: F) -> ThreadHandle
|
||||
where F: 'static + FnOnce(Waiter, Spawner) + Send {
|
||||
let handle = unsafe { Thread::new(self.clone(), stack_size, f) };
|
||||
self.queue.borrow_mut().push(handle.clone());
|
||||
handle
|
||||
}
|
||||
}
|
||||
|
||||
enum WaitEvent {
|
||||
Completion(*const (Fn() -> bool + 'static)),
|
||||
Termination(*const RefCell<Thread>),
|
||||
UdpReadable(*const RefCell<lwip::UdpSocketState>),
|
||||
TcpAcceptable(*const RefCell<lwip::TcpListenerState>),
|
||||
TcpWriteable(*const RefCell<lwip::TcpStreamState>),
|
||||
@ -115,6 +184,10 @@ enum WaitEvent {
|
||||
impl WaitEvent {
|
||||
fn completed(&self) -> bool {
|
||||
match *self {
|
||||
WaitEvent::Completion(f) =>
|
||||
unsafe { (*f)() },
|
||||
WaitEvent::Termination(thread) =>
|
||||
unsafe { (*thread).borrow().terminated() },
|
||||
WaitEvent::UdpReadable(state) =>
|
||||
unsafe { (*state).borrow().readable() },
|
||||
WaitEvent::TcpAcceptable(state) =>
|
||||
@ -127,15 +200,23 @@ impl WaitEvent {
|
||||
}
|
||||
}
|
||||
|
||||
// *const DST doesn't have impl Debug
|
||||
impl ::core::fmt::Debug for WaitEvent {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) ->
|
||||
::core::result::Result<(), ::core::fmt::Error> {
|
||||
write!(f, "WaitEvent...")
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for WaitEvent {}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Waiter<'a>(&'a Yielder<WaitResult, WaitRequest, OwnedStack>);
|
||||
|
||||
impl<'a> Waiter<'a> {
|
||||
pub fn sleep(&self, duration: Duration) -> Result<()> {
|
||||
pub fn sleep(&self, duration_ms: u64) -> Result<()> {
|
||||
let request = WaitRequest {
|
||||
timeout: Some(Instant::now() + duration),
|
||||
timeout: Some(clock::get_ms() + duration_ms),
|
||||
event: None
|
||||
};
|
||||
|
||||
@ -154,6 +235,27 @@ impl<'a> Waiter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn relinquish(&self) -> Result<()> {
|
||||
self.suspend(WaitRequest {
|
||||
timeout: None,
|
||||
event: None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn join(&self, thread: ThreadHandle) -> Result<()> {
|
||||
self.suspend(WaitRequest {
|
||||
timeout: None,
|
||||
event: Some(WaitEvent::Termination(&*thread.0))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn until<F: Fn() -> bool + 'static>(&self, f: F) -> Result<()> {
|
||||
self.suspend(WaitRequest {
|
||||
timeout: None,
|
||||
event: Some(WaitEvent::Completion(&f as *const _))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn udp_readable(&self, socket: &lwip::UdpSocket) -> Result<()> {
|
||||
self.suspend(WaitRequest {
|
||||
timeout: None,
|
||||
@ -185,7 +287,7 @@ impl<'a> Waiter<'a> {
|
||||
|
||||
// Wrappers around lwip
|
||||
|
||||
pub use self::lwip::{IpAddr, IP4_ANY, IP6_ANY, IP_ANY, SocketAddr};
|
||||
pub use lwip::{IpAddr, IP4_ANY, IP6_ANY, IP_ANY, SocketAddr};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UdpSocket<'a> {
|
||||
@ -201,6 +303,14 @@ impl<'a> UdpSocket<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn into_lower(self) -> lwip::UdpSocket {
|
||||
self.lower
|
||||
}
|
||||
|
||||
pub fn from_lower(waiter: Waiter<'a>, inner: lwip::UdpSocket) -> UdpSocket {
|
||||
UdpSocket { waiter: waiter, lower: inner }
|
||||
}
|
||||
|
||||
pub fn bind(&self, addr: SocketAddr) -> Result<()> {
|
||||
Ok(try!(self.lower.bind(addr)))
|
||||
}
|
||||
@ -218,12 +328,12 @@ impl<'a> UdpSocket<'a> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
pub fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr)> {
|
||||
pub fn recv_from(&self, buf: &mut Vec<u8>) -> Result<SocketAddr> {
|
||||
try!(self.waiter.udp_readable(&self.lower));
|
||||
let (pbuf, addr) = self.lower.try_recv().unwrap();
|
||||
let len = ::std::cmp::min(buf.len(), pbuf.len());
|
||||
(&mut buf[..len]).copy_from_slice(&pbuf.as_slice()[..len]);
|
||||
Ok((len, addr))
|
||||
buf.clear();
|
||||
buf.extend_from_slice(&pbuf.as_slice());
|
||||
Ok(addr)
|
||||
}
|
||||
|
||||
pub fn send(&self, buf: &[u8]) -> Result<usize> {
|
||||
@ -239,6 +349,10 @@ impl<'a> UdpSocket<'a> {
|
||||
(&mut buf[..len]).copy_from_slice(&pbuf.as_slice()[..len]);
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
pub fn readable(&self) -> bool {
|
||||
self.lower.state().borrow().readable()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -255,6 +369,14 @@ impl<'a> TcpListener<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn into_lower(self) -> lwip::TcpListener {
|
||||
self.lower
|
||||
}
|
||||
|
||||
pub fn from_lower(waiter: Waiter<'a>, inner: lwip::TcpListener) -> TcpListener {
|
||||
TcpListener { waiter: waiter, lower: inner }
|
||||
}
|
||||
|
||||
pub fn accept(&self) -> Result<(TcpStream, SocketAddr)> {
|
||||
try!(self.waiter.tcp_acceptable(&self.lower));
|
||||
let stream_lower = self.lower.try_accept().unwrap();
|
||||
@ -265,9 +387,23 @@ impl<'a> TcpListener<'a> {
|
||||
buffer: None
|
||||
}, addr))
|
||||
}
|
||||
|
||||
pub fn acceptable(&self) -> bool {
|
||||
self.lower.state().borrow().acceptable()
|
||||
}
|
||||
|
||||
pub fn keepalive(&self) -> bool {
|
||||
self.lower.keepalive()
|
||||
}
|
||||
|
||||
pub fn set_keepalive(&self, keepalive: bool) {
|
||||
self.lower.set_keepalive(keepalive)
|
||||
}
|
||||
}
|
||||
|
||||
pub use self::lwip::Shutdown;
|
||||
pub use lwip::Shutdown;
|
||||
|
||||
pub struct TcpStreamInner(lwip::TcpStream, Option<(lwip::Pbuf<'static>, usize)>);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TcpStream<'a> {
|
||||
@ -277,23 +413,43 @@ pub struct TcpStream<'a> {
|
||||
}
|
||||
|
||||
impl<'a> TcpStream<'a> {
|
||||
pub fn into_lower(self) -> TcpStreamInner {
|
||||
TcpStreamInner(self.lower, self.buffer)
|
||||
}
|
||||
|
||||
pub fn from_lower(waiter: Waiter<'a>, inner: TcpStreamInner) -> TcpStream {
|
||||
TcpStream { waiter: waiter, lower: inner.0, buffer: inner.1 }
|
||||
}
|
||||
|
||||
pub fn shutdown(&self, how: Shutdown) -> Result<()> {
|
||||
Ok(try!(self.lower.shutdown(how)))
|
||||
}
|
||||
|
||||
pub fn readable(&self) -> bool {
|
||||
self.buffer.is_some() || self.lower.state().borrow().readable()
|
||||
}
|
||||
|
||||
pub fn writeable(&self) -> bool {
|
||||
self.lower.state().borrow().writeable()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Read for TcpStream<'a> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
if self.buffer.is_none() {
|
||||
try!(self.waiter.tcp_readable(&self.lower));
|
||||
let pbuf = try!(self.lower.try_read()).unwrap();
|
||||
self.buffer = Some((pbuf, 0))
|
||||
match self.lower.try_read() {
|
||||
Ok(Some(pbuf)) => self.buffer = Some((pbuf, 0)),
|
||||
Ok(None) => unreachable!(),
|
||||
Err(lwip::Error::ConnectionClosed) => return Ok(0),
|
||||
Err(err) => return Err(Error::from(err))
|
||||
}
|
||||
}
|
||||
|
||||
let (pbuf, pos) = self.buffer.take().unwrap();
|
||||
let slice = &pbuf.as_slice()[pos..];
|
||||
let len = ::std::cmp::min(buf.len(), slice.len());
|
||||
buf.copy_from_slice(&slice[..len]);
|
||||
buf[..len].copy_from_slice(&slice[..len]);
|
||||
if len < slice.len() {
|
||||
self.buffer = Some((pbuf, pos + len))
|
||||
}
|
||||
@ -304,7 +460,9 @@ impl<'a> Read for TcpStream<'a> {
|
||||
impl<'a> Write for TcpStream<'a> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
try!(self.waiter.tcp_writeable(&self.lower));
|
||||
Ok(try!(self.lower.write(buf)))
|
||||
Ok(try!(self.lower.write_in_place(buf,
|
||||
|| self.waiter.relinquish()
|
||||
.map_err(|_| lwip::Error::Interrupted))))
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<()> {
|
641
artiq/runtime.rs/src/session.rs
Normal file
641
artiq/runtime.rs/src/session.rs
Normal file
@ -0,0 +1,641 @@
|
||||
use std::prelude::v1::*;
|
||||
use std::{mem, str};
|
||||
use std::cell::RefCell;
|
||||
use std::io::{self, Read, Write, BufWriter};
|
||||
use {config, rtio_crg, clock, mailbox, rpc_queue, kernel};
|
||||
use logger::BufferLogger;
|
||||
use cache::Cache;
|
||||
use urc::Urc;
|
||||
use sched::{ThreadHandle, Waiter, Spawner};
|
||||
use sched::{TcpListener, TcpStream, SocketAddr, IP_ANY};
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
|
||||
use rpc_proto as rpc;
|
||||
use session_proto as host;
|
||||
use kernel_proto as kern;
|
||||
|
||||
macro_rules! unexpected {
|
||||
($($arg:tt)*) => {
|
||||
{
|
||||
error!($($arg)*);
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "protocol error"))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn io_error(msg: &str) -> io::Error {
|
||||
io::Error::new(io::ErrorKind::Other, msg)
|
||||
}
|
||||
|
||||
// Persistent state
|
||||
#[derive(Debug)]
|
||||
struct Congress {
|
||||
now: u64,
|
||||
cache: Cache
|
||||
}
|
||||
|
||||
impl Congress {
|
||||
fn new() -> Congress {
|
||||
Congress {
|
||||
now: 0,
|
||||
cache: Cache::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum KernelState {
|
||||
Absent,
|
||||
Loaded,
|
||||
Running,
|
||||
RpcWait
|
||||
}
|
||||
|
||||
// Per-connection state
|
||||
#[derive(Debug)]
|
||||
struct Session<'a> {
|
||||
congress: &'a mut Congress,
|
||||
kernel_state: KernelState,
|
||||
watchdog_set: clock::WatchdogSet,
|
||||
log_buffer: String
|
||||
}
|
||||
|
||||
impl<'a> Session<'a> {
|
||||
fn new(congress: &mut Congress) -> Session {
|
||||
Session {
|
||||
congress: congress,
|
||||
kernel_state: KernelState::Absent,
|
||||
watchdog_set: clock::WatchdogSet::new(),
|
||||
log_buffer: String::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn running(&self) -> bool {
|
||||
match self.kernel_state {
|
||||
KernelState::Absent | KernelState::Loaded => false,
|
||||
KernelState::Running | KernelState::RpcWait => true
|
||||
}
|
||||
}
|
||||
|
||||
fn flush_log_buffer(&mut self) {
|
||||
if &self.log_buffer[self.log_buffer.len() - 1..] == "\n" {
|
||||
for line in self.log_buffer.lines() {
|
||||
info!(target: "kernel", "{}", line);
|
||||
}
|
||||
self.log_buffer.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for Session<'a> {
|
||||
fn drop(&mut self) {
|
||||
kernel::stop()
|
||||
}
|
||||
}
|
||||
|
||||
fn check_magic(stream: &mut TcpStream) -> io::Result<()> {
|
||||
const MAGIC: &'static [u8] = b"ARTIQ coredev\n";
|
||||
|
||||
let mut magic: [u8; 14] = [0; 14];
|
||||
try!(stream.read_exact(&mut magic));
|
||||
if magic != MAGIC {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidData, "unrecognized magic"))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn host_read(stream: &mut TcpStream) -> io::Result<host::Request> {
|
||||
let request = try!(host::Request::read_from(stream));
|
||||
match &request {
|
||||
&host::Request::LoadKernel(_) => trace!("comm<-host LoadLibrary(...)"),
|
||||
_ => trace!("comm<-host {:?}", request)
|
||||
}
|
||||
Ok(request)
|
||||
}
|
||||
|
||||
fn host_write(stream: &mut Write, reply: host::Reply) -> io::Result<()> {
|
||||
trace!("comm->host {:?}", reply);
|
||||
reply.write_to(stream)
|
||||
}
|
||||
|
||||
fn kern_send(waiter: Waiter, request: &kern::Message) -> io::Result<()> {
|
||||
match request {
|
||||
&kern::LoadRequest(_) => trace!("comm->kern LoadRequest(...)"),
|
||||
_ => trace!("comm->kern {:?}", request)
|
||||
}
|
||||
unsafe { mailbox::send(request as *const _ as usize) }
|
||||
waiter.until(mailbox::acknowledged)
|
||||
}
|
||||
|
||||
fn kern_recv_notrace<R, F>(waiter: Waiter, f: F) -> io::Result<R>
|
||||
where F: FnOnce(&kern::Message) -> io::Result<R> {
|
||||
try!(waiter.until(|| mailbox::receive() != 0));
|
||||
if !kernel::validate(mailbox::receive()) {
|
||||
let message = format!("invalid kernel CPU pointer 0x{:x}", mailbox::receive());
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, message))
|
||||
}
|
||||
|
||||
f(unsafe { mem::transmute::<usize, &kern::Message>(mailbox::receive()) })
|
||||
}
|
||||
|
||||
fn kern_recv_dotrace(reply: &kern::Message) {
|
||||
match reply {
|
||||
&kern::Log(_) => trace!("comm<-kern Log(...)"),
|
||||
&kern::LogSlice(_) => trace!("comm<-kern LogSlice(...)"),
|
||||
_ => trace!("comm<-kern {:?}", reply)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn kern_recv<R, F>(waiter: Waiter, f: F) -> io::Result<R>
|
||||
where F: FnOnce(&kern::Message) -> io::Result<R> {
|
||||
kern_recv_notrace(waiter, |reply| {
|
||||
kern_recv_dotrace(reply);
|
||||
f(reply)
|
||||
})
|
||||
}
|
||||
|
||||
fn kern_acknowledge() -> io::Result<()> {
|
||||
mailbox::acknowledge();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn kern_load(waiter: Waiter, session: &mut Session, library: &[u8]) -> io::Result<()> {
|
||||
if session.running() {
|
||||
unexpected!("attempted to load a new kernel while a kernel was running")
|
||||
}
|
||||
|
||||
kernel::start();
|
||||
|
||||
try!(kern_send(waiter, &kern::LoadRequest(&library)));
|
||||
kern_recv(waiter, |reply| {
|
||||
match reply {
|
||||
&kern::LoadReply(Ok(())) => {
|
||||
session.kernel_state = KernelState::Loaded;
|
||||
Ok(())
|
||||
}
|
||||
&kern::LoadReply(Err(error)) =>
|
||||
unexpected!("cannot load kernel: {}", error),
|
||||
other =>
|
||||
unexpected!("unexpected reply from kernel CPU: {:?}", other)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn kern_run(session: &mut Session) -> io::Result<()> {
|
||||
if session.kernel_state != KernelState::Loaded {
|
||||
unexpected!("attempted to run a kernel while not in Loaded state")
|
||||
}
|
||||
|
||||
session.kernel_state = KernelState::Running;
|
||||
// TODO: make this a separate request
|
||||
kern_acknowledge()
|
||||
}
|
||||
|
||||
fn process_host_message(waiter: Waiter,
|
||||
stream: &mut TcpStream,
|
||||
session: &mut Session) -> io::Result<()> {
|
||||
match try!(host_read(stream)) {
|
||||
host::Request::Ident =>
|
||||
host_write(stream, host::Reply::Ident(::board::ident(&mut [0; 64]))),
|
||||
|
||||
// artiq_corelog
|
||||
host::Request::Log => {
|
||||
// Logging the packet with the log is inadvisable
|
||||
trace!("comm->host Log(...)");
|
||||
BufferLogger::with_instance(|logger| {
|
||||
logger.extract(|log| {
|
||||
host::Reply::Log(log).write_to(stream)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
host::Request::LogClear => {
|
||||
BufferLogger::with_instance(|logger| logger.clear());
|
||||
host_write(stream, host::Reply::Log(""))
|
||||
}
|
||||
|
||||
// artiq_coreconfig
|
||||
host::Request::FlashRead { ref key } => {
|
||||
let value = config::read_to_end(key);
|
||||
host_write(stream, host::Reply::FlashRead(&value))
|
||||
}
|
||||
|
||||
host::Request::FlashWrite { ref key, ref value } => {
|
||||
match config::write(key, value) {
|
||||
Ok(_) => host_write(stream, host::Reply::FlashOk),
|
||||
Err(_) => host_write(stream, host::Reply::FlashError)
|
||||
}
|
||||
}
|
||||
|
||||
host::Request::FlashRemove { ref key } => {
|
||||
config::remove(key);
|
||||
host_write(stream, host::Reply::FlashOk)
|
||||
}
|
||||
|
||||
host::Request::FlashErase => {
|
||||
config::erase();
|
||||
host_write(stream, host::Reply::FlashOk)
|
||||
}
|
||||
|
||||
// artiq_run/artiq_master
|
||||
host::Request::SwitchClock(clk) => {
|
||||
if session.running() {
|
||||
unexpected!("attempted to switch RTIO clock while a kernel was running")
|
||||
}
|
||||
|
||||
if rtio_crg::switch_clock(clk) {
|
||||
host_write(stream, host::Reply::ClockSwitchCompleted)
|
||||
} else {
|
||||
host_write(stream, host::Reply::ClockSwitchFailed)
|
||||
}
|
||||
}
|
||||
|
||||
host::Request::LoadKernel(kernel) =>
|
||||
match unsafe { kern_load(waiter, session, &kernel) } {
|
||||
Ok(()) => host_write(stream, host::Reply::LoadCompleted),
|
||||
Err(_) => {
|
||||
try!(kern_acknowledge());
|
||||
host_write(stream, host::Reply::LoadFailed)
|
||||
}
|
||||
},
|
||||
|
||||
host::Request::RunKernel =>
|
||||
match kern_run(session) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(_) => host_write(stream, host::Reply::KernelStartupFailed)
|
||||
},
|
||||
|
||||
host::Request::RpcReply { tag } => {
|
||||
if session.kernel_state != KernelState::RpcWait {
|
||||
unexpected!("unsolicited RPC reply")
|
||||
}
|
||||
|
||||
let slot = try!(kern_recv(waiter, |reply| {
|
||||
match reply {
|
||||
&kern::RpcRecvRequest(slot) => Ok(slot),
|
||||
other => unexpected!("unexpected reply from kernel CPU: {:?}", other)
|
||||
}
|
||||
}));
|
||||
try!(rpc::recv_return(stream, &tag, slot, &|size| {
|
||||
try!(kern_send(waiter, &kern::RpcRecvReply(Ok(size))));
|
||||
kern_recv(waiter, |reply| {
|
||||
match reply {
|
||||
&kern::RpcRecvRequest(slot) => Ok(slot),
|
||||
other => unexpected!("unexpected reply from kernel CPU: {:?}", other)
|
||||
}
|
||||
})
|
||||
}));
|
||||
try!(kern_send(waiter, &kern::RpcRecvReply(Ok(0))));
|
||||
|
||||
session.kernel_state = KernelState::Running;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
host::Request::RpcException {
|
||||
name, message, param, file, line, column, function
|
||||
} => {
|
||||
if session.kernel_state != KernelState::RpcWait {
|
||||
unexpected!("unsolicited RPC reply")
|
||||
}
|
||||
|
||||
try!(kern_recv(waiter, |reply| {
|
||||
match reply {
|
||||
&kern::RpcRecvRequest(_) => Ok(()),
|
||||
other =>
|
||||
unexpected!("unexpected reply from kernel CPU: {:?}", other)
|
||||
}
|
||||
}));
|
||||
|
||||
// FIXME: gross.
|
||||
fn into_c_str(s: String) -> *const u8 {
|
||||
let s = s + "\0";
|
||||
let p = s.as_bytes().as_ptr();
|
||||
mem::forget(s);
|
||||
p
|
||||
}
|
||||
let exn = kern::Exception {
|
||||
name: into_c_str(name),
|
||||
message: into_c_str(message),
|
||||
param: param,
|
||||
file: into_c_str(file),
|
||||
line: line,
|
||||
column: column,
|
||||
function: into_c_str(function),
|
||||
phantom: ::core::marker::PhantomData
|
||||
};
|
||||
try!(kern_send(waiter, &kern::RpcRecvReply(Err(exn))));
|
||||
|
||||
session.kernel_state = KernelState::Running;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_kern_message(waiter: Waiter,
|
||||
mut stream: Option<&mut TcpStream>,
|
||||
session: &mut Session) -> io::Result<bool> {
|
||||
kern_recv_notrace(waiter, |request| {
|
||||
match (request, session.kernel_state) {
|
||||
(&kern::LoadReply(_), KernelState::Loaded) |
|
||||
(&kern::RpcRecvRequest(_), KernelState::RpcWait) => {
|
||||
// We're standing by; ignore the message.
|
||||
return Ok(false)
|
||||
}
|
||||
(_, KernelState::Running) => (),
|
||||
_ => {
|
||||
unexpected!("unexpected request {:?} from kernel CPU in {:?} state",
|
||||
request, session.kernel_state)
|
||||
}
|
||||
}
|
||||
|
||||
kern_recv_dotrace(request);
|
||||
match request {
|
||||
&kern::Log(args) => {
|
||||
use std::fmt::Write;
|
||||
try!(session.log_buffer.write_fmt(args)
|
||||
.map_err(|_| io_error("cannot append to session log buffer")));
|
||||
session.flush_log_buffer();
|
||||
kern_acknowledge()
|
||||
}
|
||||
|
||||
&kern::LogSlice(arg) => {
|
||||
session.log_buffer += arg;
|
||||
session.flush_log_buffer();
|
||||
kern_acknowledge()
|
||||
}
|
||||
|
||||
&kern::NowInitRequest =>
|
||||
kern_send(waiter, &kern::NowInitReply(session.congress.now)),
|
||||
|
||||
&kern::NowSave(now) => {
|
||||
session.congress.now = now;
|
||||
kern_acknowledge()
|
||||
}
|
||||
|
||||
&kern::WatchdogSetRequest { ms } => {
|
||||
let id = try!(session.watchdog_set.set_ms(ms)
|
||||
.map_err(|()| io_error("out of watchdogs")));
|
||||
kern_send(waiter, &kern::WatchdogSetReply { id: id })
|
||||
}
|
||||
|
||||
&kern::WatchdogClear { id } => {
|
||||
session.watchdog_set.clear(id);
|
||||
kern_acknowledge()
|
||||
}
|
||||
|
||||
&kern::RpcSend { async, service, tag, data } => {
|
||||
match stream {
|
||||
None => unexpected!("unexpected RPC in flash kernel"),
|
||||
Some(ref mut stream) => {
|
||||
let writer = &mut BufWriter::new(stream);
|
||||
try!(host_write(writer, host::Reply::RpcRequest { async: async }));
|
||||
try!(rpc::send_args(writer, service, tag, data));
|
||||
if !async {
|
||||
session.kernel_state = KernelState::RpcWait
|
||||
}
|
||||
kern_acknowledge()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&kern::CacheGetRequest { key } => {
|
||||
let value = session.congress.cache.get(key);
|
||||
kern_send(waiter, &kern::CacheGetReply {
|
||||
value: unsafe { mem::transmute::<*const [u32], &'static [u32]>(value) }
|
||||
})
|
||||
}
|
||||
|
||||
&kern::CachePutRequest { key, value } => {
|
||||
let succeeded = session.congress.cache.put(key, value).is_ok();
|
||||
kern_send(waiter, &kern::CachePutReply { succeeded: succeeded })
|
||||
}
|
||||
|
||||
&kern::RunFinished => {
|
||||
kernel::stop();
|
||||
session.kernel_state = KernelState::Absent;
|
||||
unsafe { session.congress.cache.unborrow() }
|
||||
|
||||
match stream {
|
||||
None => return Ok(true),
|
||||
Some(ref mut stream) =>
|
||||
host_write(stream, host::Reply::KernelFinished)
|
||||
}
|
||||
}
|
||||
|
||||
&kern::RunException { exception: ref exn, backtrace } => {
|
||||
kernel::stop();
|
||||
session.kernel_state = KernelState::Absent;
|
||||
unsafe { session.congress.cache.unborrow() }
|
||||
|
||||
unsafe fn from_c_str<'a>(s: *const u8) -> &'a str {
|
||||
use ::libc::{c_char, size_t};
|
||||
use core::slice;
|
||||
extern { fn strlen(s: *const c_char) -> size_t; }
|
||||
let s = slice::from_raw_parts(s, strlen(s as *const c_char));
|
||||
str::from_utf8_unchecked(s)
|
||||
}
|
||||
let name = unsafe { from_c_str(exn.name) };
|
||||
let message = unsafe { from_c_str(exn.message) };
|
||||
let file = unsafe { from_c_str(exn.file) };
|
||||
let function = unsafe { from_c_str(exn.function) };
|
||||
match stream {
|
||||
None => {
|
||||
error!("exception in flash kernel");
|
||||
error!("{}: {} {:?}", name, message, exn.param);
|
||||
error!("at {}:{}:{} in {}", file, exn.line, exn.column, function);
|
||||
return Ok(true)
|
||||
},
|
||||
Some(ref mut stream) =>
|
||||
host_write(stream, host::Reply::KernelException {
|
||||
name: name,
|
||||
message: message,
|
||||
param: exn.param,
|
||||
file: file,
|
||||
line: exn.line,
|
||||
column: exn.column,
|
||||
function: function,
|
||||
backtrace: backtrace
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
request => unexpected!("unexpected request {:?} from kernel CPU", request)
|
||||
}.and(Ok(false))
|
||||
})
|
||||
}
|
||||
|
||||
fn process_kern_queued_rpc(stream: &mut TcpStream,
|
||||
session: &mut Session) -> io::Result<()> {
|
||||
rpc_queue::dequeue(|slice| {
|
||||
trace!("comm<-kern (async RPC)");
|
||||
let length = NetworkEndian::read_u32(slice) as usize;
|
||||
try!(host_write(stream, host::Reply::RpcRequest { async: true }));
|
||||
trace!("{:?}" ,&slice[4..][..length]);
|
||||
try!(stream.write(&slice[4..][..length]));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn host_kernel_worker(waiter: Waiter,
|
||||
stream: &mut TcpStream,
|
||||
congress: &mut Congress) -> io::Result<()> {
|
||||
let mut session = Session::new(congress);
|
||||
|
||||
loop {
|
||||
while !rpc_queue::empty() {
|
||||
try!(process_kern_queued_rpc(stream, &mut session))
|
||||
}
|
||||
|
||||
if stream.readable() {
|
||||
try!(process_host_message(waiter, stream, &mut session));
|
||||
}
|
||||
|
||||
if mailbox::receive() != 0 {
|
||||
try!(process_kern_message(waiter, Some(stream), &mut session));
|
||||
}
|
||||
|
||||
if session.kernel_state == KernelState::Running {
|
||||
if session.watchdog_set.expired() {
|
||||
try!(host_write(stream, host::Reply::WatchdogExpired));
|
||||
return Err(io_error("watchdog expired"))
|
||||
}
|
||||
|
||||
if !rtio_crg::check() {
|
||||
try!(host_write(stream, host::Reply::ClockFailure));
|
||||
return Err(io_error("RTIO clock failure"))
|
||||
}
|
||||
}
|
||||
|
||||
try!(waiter.relinquish())
|
||||
}
|
||||
}
|
||||
|
||||
fn flash_kernel_worker(waiter: Waiter,
|
||||
congress: &mut Congress,
|
||||
config_key: &str) -> io::Result<()> {
|
||||
let mut session = Session::new(congress);
|
||||
|
||||
let kernel = config::read_to_end(config_key);
|
||||
if kernel.len() == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::NotFound, "kernel not found"))
|
||||
}
|
||||
|
||||
try!(unsafe { kern_load(waiter, &mut session, &kernel) });
|
||||
try!(kern_run(&mut session));
|
||||
|
||||
loop {
|
||||
if !rpc_queue::empty() {
|
||||
return Err(io_error("unexpected background RPC in flash kernel"))
|
||||
}
|
||||
|
||||
if mailbox::receive() != 0 {
|
||||
if try!(process_kern_message(waiter, None, &mut session)) {
|
||||
return Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
if session.watchdog_set.expired() {
|
||||
return Err(io_error("watchdog expired"))
|
||||
}
|
||||
|
||||
if !rtio_crg::check() {
|
||||
return Err(io_error("RTIO clock failure"))
|
||||
}
|
||||
|
||||
try!(waiter.relinquish())
|
||||
}
|
||||
}
|
||||
|
||||
fn respawn<F>(spawner: Spawner, waiter: Waiter,
|
||||
handle: &mut Option<ThreadHandle>,
|
||||
f: F) where F: 'static + FnOnce(Waiter, Spawner) + Send {
|
||||
match handle.take() {
|
||||
None => (),
|
||||
Some(handle) => {
|
||||
if !handle.terminated() {
|
||||
info!("terminating running kernel");
|
||||
handle.interrupt();
|
||||
waiter.join(handle).expect("cannot join interrupt thread")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*handle = Some(spawner.spawn(16384, f))
|
||||
}
|
||||
|
||||
pub fn thread(waiter: Waiter, spawner: Spawner) {
|
||||
let congress = Urc::new(RefCell::new(Congress::new()));
|
||||
|
||||
info!("running startup kernel");
|
||||
match flash_kernel_worker(waiter, &mut congress.borrow_mut(), "startup_kernel") {
|
||||
Ok(()) => info!("startup kernel finished"),
|
||||
Err(err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
info!("no startup kernel found")
|
||||
} else {
|
||||
error!("startup kernel aborted: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let addr = SocketAddr::new(IP_ANY, 1381);
|
||||
let listener = TcpListener::bind(waiter, addr).expect("cannot bind socket");
|
||||
listener.set_keepalive(true);
|
||||
info!("accepting network sessions in Rust");
|
||||
|
||||
let mut kernel_thread = None;
|
||||
loop {
|
||||
if listener.acceptable() {
|
||||
let (mut stream, addr) = listener.accept().expect("cannot accept client");
|
||||
match check_magic(&mut stream) {
|
||||
Ok(()) => (),
|
||||
Err(_) => continue
|
||||
}
|
||||
info!("new connection from {}", addr);
|
||||
|
||||
let stream = stream.into_lower();
|
||||
let congress = congress.clone();
|
||||
respawn(spawner.clone(), waiter, &mut kernel_thread, move |waiter, _spawner| {
|
||||
let mut stream = TcpStream::from_lower(waiter, stream);
|
||||
let mut congress = congress.borrow_mut();
|
||||
match host_kernel_worker(waiter, &mut stream, &mut congress) {
|
||||
Ok(()) => (),
|
||||
Err(err) => {
|
||||
if err.kind() == io::ErrorKind::UnexpectedEof {
|
||||
info!("connection closed");
|
||||
} else {
|
||||
error!("session aborted: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if kernel_thread.as_ref().map_or(true, |h| h.terminated()) {
|
||||
info!("no connection, starting idle kernel");
|
||||
|
||||
let congress = congress.clone();
|
||||
respawn(spawner.clone(), waiter, &mut kernel_thread, move |waiter, _spawner| {
|
||||
let mut congress = congress.borrow_mut();
|
||||
match flash_kernel_worker(waiter, &mut congress, "idle_kernel") {
|
||||
Ok(()) =>
|
||||
info!("idle kernel finished, standing by"),
|
||||
Err(err) => {
|
||||
if err.kind() == io::ErrorKind::Interrupted {
|
||||
info!("idle kernel interrupted");
|
||||
} else if err.kind() == io::ErrorKind::NotFound {
|
||||
info!("no idle kernel found");
|
||||
while waiter.relinquish().is_ok() {}
|
||||
} else {
|
||||
error!("idle kernel aborted: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let _ = waiter.relinquish();
|
||||
}
|
||||
}
|
198
artiq/runtime.rs/src/session_proto.rs
Normal file
198
artiq/runtime.rs/src/session_proto.rs
Normal file
@ -0,0 +1,198 @@
|
||||
use std::prelude::v1::*;
|
||||
use std::io::{self, Read, Write};
|
||||
use proto::*;
|
||||
|
||||
fn read_sync(reader: &mut Read) -> io::Result<()> {
|
||||
let mut sync = [0; 4];
|
||||
for i in 0.. {
|
||||
sync[i % 4] = try!(read_u8(reader));
|
||||
if sync == [0x5a; 4] { break }
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_sync(writer: &mut Write) -> io::Result<()> {
|
||||
writer.write_all(&[0x5a; 4])
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Request {
|
||||
Log,
|
||||
LogClear,
|
||||
|
||||
Ident,
|
||||
SwitchClock(u8),
|
||||
|
||||
LoadKernel(Vec<u8>),
|
||||
RunKernel,
|
||||
|
||||
RpcReply { tag: Vec<u8> },
|
||||
RpcException {
|
||||
name: String,
|
||||
message: String,
|
||||
param: [u64; 3],
|
||||
file: String,
|
||||
line: u32,
|
||||
column: u32,
|
||||
function: String,
|
||||
},
|
||||
|
||||
FlashRead { key: String },
|
||||
FlashWrite { key: String, value: Vec<u8> },
|
||||
FlashRemove { key: String },
|
||||
FlashErase,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
pub fn read_from(reader: &mut Read) -> io::Result<Request> {
|
||||
try!(read_sync(reader));
|
||||
Ok(match try!(read_u8(reader)) {
|
||||
1 => Request::Log,
|
||||
2 => Request::LogClear,
|
||||
3 => Request::Ident,
|
||||
4 => Request::SwitchClock(try!(read_u8(reader))),
|
||||
5 => Request::LoadKernel(try!(read_bytes(reader))),
|
||||
6 => Request::RunKernel,
|
||||
7 => Request::RpcReply {
|
||||
tag: try!(read_bytes(reader))
|
||||
},
|
||||
8 => Request::RpcException {
|
||||
name: try!(read_string(reader)),
|
||||
message: try!(read_string(reader)),
|
||||
param: [try!(read_u64(reader)),
|
||||
try!(read_u64(reader)),
|
||||
try!(read_u64(reader))],
|
||||
file: try!(read_string(reader)),
|
||||
line: try!(read_u32(reader)),
|
||||
column: try!(read_u32(reader)),
|
||||
function: try!(read_string(reader))
|
||||
},
|
||||
9 => Request::FlashRead {
|
||||
key: try!(read_string(reader))
|
||||
},
|
||||
10 => Request::FlashWrite {
|
||||
key: try!(read_string(reader)),
|
||||
value: try!(read_bytes(reader))
|
||||
},
|
||||
11 => Request::FlashErase,
|
||||
12 => Request::FlashRemove {
|
||||
key: try!(read_string(reader))
|
||||
},
|
||||
_ => return Err(io::Error::new(io::ErrorKind::InvalidData, "unknown request type"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Reply<'a> {
|
||||
Log(&'a str),
|
||||
|
||||
Ident(&'a str),
|
||||
ClockSwitchCompleted,
|
||||
ClockSwitchFailed,
|
||||
|
||||
LoadCompleted,
|
||||
LoadFailed,
|
||||
|
||||
KernelFinished,
|
||||
KernelStartupFailed,
|
||||
KernelException {
|
||||
name: &'a str,
|
||||
message: &'a str,
|
||||
param: [u64; 3],
|
||||
file: &'a str,
|
||||
line: u32,
|
||||
column: u32,
|
||||
function: &'a str,
|
||||
backtrace: &'a [usize]
|
||||
},
|
||||
|
||||
RpcRequest { async: bool },
|
||||
|
||||
FlashRead(&'a [u8]),
|
||||
FlashOk,
|
||||
FlashError,
|
||||
|
||||
WatchdogExpired,
|
||||
ClockFailure,
|
||||
}
|
||||
|
||||
impl<'a> Reply<'a> {
|
||||
pub fn write_to(&self, writer: &mut Write) -> io::Result<()> {
|
||||
try!(write_sync(writer));
|
||||
match *self {
|
||||
Reply::Log(ref log) => {
|
||||
try!(write_u8(writer, 1));
|
||||
try!(write_string(writer, log));
|
||||
},
|
||||
|
||||
Reply::Ident(ident) => {
|
||||
try!(write_u8(writer, 2));
|
||||
try!(writer.write(b"AROR"));
|
||||
try!(write_string(writer, ident));
|
||||
},
|
||||
Reply::ClockSwitchCompleted => {
|
||||
try!(write_u8(writer, 3));
|
||||
},
|
||||
Reply::ClockSwitchFailed => {
|
||||
try!(write_u8(writer, 4));
|
||||
},
|
||||
|
||||
Reply::LoadCompleted => {
|
||||
try!(write_u8(writer, 5));
|
||||
},
|
||||
Reply::LoadFailed => {
|
||||
try!(write_u8(writer, 6));
|
||||
},
|
||||
|
||||
Reply::KernelFinished => {
|
||||
try!(write_u8(writer, 7));
|
||||
},
|
||||
Reply::KernelStartupFailed => {
|
||||
try!(write_u8(writer, 8));
|
||||
},
|
||||
Reply::KernelException {
|
||||
name, message, param, file, line, column, function, backtrace
|
||||
} => {
|
||||
try!(write_u8(writer, 9));
|
||||
try!(write_string(writer, name));
|
||||
try!(write_string(writer, message));
|
||||
try!(write_u64(writer, param[0]));
|
||||
try!(write_u64(writer, param[1]));
|
||||
try!(write_u64(writer, param[2]));
|
||||
try!(write_string(writer, file));
|
||||
try!(write_u32(writer, line));
|
||||
try!(write_u32(writer, column));
|
||||
try!(write_string(writer, function));
|
||||
try!(write_u32(writer, backtrace.len() as u32));
|
||||
for &addr in backtrace {
|
||||
try!(write_u32(writer, addr as u32))
|
||||
}
|
||||
},
|
||||
|
||||
Reply::RpcRequest { async } => {
|
||||
try!(write_u8(writer, 10));
|
||||
try!(write_u8(writer, async as u8));
|
||||
},
|
||||
|
||||
Reply::FlashRead(ref bytes) => {
|
||||
try!(write_u8(writer, 11));
|
||||
try!(write_bytes(writer, bytes));
|
||||
},
|
||||
Reply::FlashOk => {
|
||||
try!(write_u8(writer, 12));
|
||||
},
|
||||
Reply::FlashError => {
|
||||
try!(write_u8(writer, 13));
|
||||
},
|
||||
|
||||
Reply::WatchdogExpired => {
|
||||
try!(write_u8(writer, 14));
|
||||
},
|
||||
Reply::ClockFailure => {
|
||||
try!(write_u8(writer, 15));
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
31
artiq/runtime.rs/src/urc.rs
Normal file
31
artiq/runtime.rs/src/urc.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use std::rc::Rc;
|
||||
use std::ops::Deref;
|
||||
use std::fmt;
|
||||
|
||||
pub struct Urc<T: ?Sized>(Rc<T>);
|
||||
|
||||
impl<T> Urc<T> {
|
||||
pub fn new(value: T) -> Urc<T> { Urc(Rc::new(value)) }
|
||||
}
|
||||
|
||||
unsafe impl<T: ?Sized> Send for Urc<T> {}
|
||||
|
||||
unsafe impl<T: ?Sized> Sync for Urc<T> {}
|
||||
|
||||
impl<T: ?Sized> Deref for Urc<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target { self.0.deref() }
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Clone for Urc<T> {
|
||||
fn clone(&self) -> Urc<T> {
|
||||
Urc(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for Urc<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(&**self, f)
|
||||
}
|
||||
}
|
@ -3,23 +3,75 @@ include $(MISOC_DIRECTORY)/software/common.mak
|
||||
|
||||
PYTHON ?= python3.5
|
||||
|
||||
OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \
|
||||
session.o log.o analyzer.o moninj.o net_server.o bridge_ctl.o \
|
||||
ksupport_data.o kloader.o test_mode.o main.o
|
||||
OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \
|
||||
bridge.o rtio.o dds.o i2c.o ad9154.o
|
||||
OBJECTS := flash_storage.o main.o
|
||||
OBJECTS_KSUPPORT := ksupport_glue.o artiq_personality.o rtio.o dds.o i2c.o ad9154.o
|
||||
|
||||
CFLAGS += -I$(LIBALLOC_DIRECTORY) \
|
||||
RUSTOUT_DIRECTORY := cargo/or1k-unknown-none/debug
|
||||
CORE_IO_COMMIT := d40c593f42fafbac1ff3d827f6df96338b5b7d8b
|
||||
export CORE_IO_COMMIT
|
||||
|
||||
CFLAGS += \
|
||||
-I$(LIBALLOC_DIRECTORY) \
|
||||
-I$(MISOC_DIRECTORY)/software/include/dyld \
|
||||
-I$(LIBDYLD_DIRECTORY)/include \
|
||||
-I$(LIBUNWIND_DIRECTORY) \
|
||||
-I$(LIBUNWIND_DIRECTORY)/../unwinder/include \
|
||||
-I$(LIBLWIP_DIRECTORY)/../lwip/src/include \
|
||||
-I$(LIBLWIP_DIRECTORY) \
|
||||
-DNDEBUG
|
||||
-I$(LIBLWIP_DIRECTORY)
|
||||
CFLAGS += -DNDEBUG
|
||||
|
||||
LDFLAGS += --gc-sections \
|
||||
-L../libcompiler-rt \
|
||||
-L../libbase \
|
||||
-L../libm \
|
||||
-L../liballoc \
|
||||
-L../libunwind \
|
||||
-L../libdyld \
|
||||
-L../liblwip
|
||||
|
||||
all: runtime.bin runtime.fbi
|
||||
|
||||
.PHONY: $(RUSTOUT_DIRECTORY)/libruntime.a
|
||||
$(RUSTOUT_DIRECTORY)/libruntime.a: ksupport.elf
|
||||
CARGO_TARGET_DIR=$(realpath .)/cargo \
|
||||
cargo rustc --verbose \
|
||||
--manifest-path $(realpath $(RUNTIME_DIRECTORY)/../runtime.rs/Cargo.toml) \
|
||||
--target=or1k-unknown-none -- \
|
||||
$(shell cat $(BUILDINC_DIRECTORY)/generated/rust-cfg) \
|
||||
-C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s \
|
||||
-L../libcompiler-rt
|
||||
|
||||
runtime.elf: $(OBJECTS) $(RUSTOUT_DIRECTORY)/libruntime.a
|
||||
$(LD) $(LDFLAGS) \
|
||||
-T $(RUNTIME_DIRECTORY)/runtime.ld \
|
||||
-o $@ \
|
||||
$^ \
|
||||
-lbase-nofloat -lcompiler-rt -lalloc -llwip
|
||||
@chmod -x $@
|
||||
|
||||
.PHONY: $(RUSTOUT_DIRECTORY)/libksupport.a
|
||||
$(RUSTOUT_DIRECTORY)/libksupport.a:
|
||||
CARGO_TARGET_DIR=$(realpath .)/cargo \
|
||||
cargo rustc --verbose \
|
||||
--manifest-path $(realpath $(RUNTIME_DIRECTORY)/../runtime.rs/libksupport/Cargo.toml) \
|
||||
--target=or1k-unknown-none -- \
|
||||
$(shell cat $(BUILDINC_DIRECTORY)/generated/rust-cfg) \
|
||||
--cfg ksupport \
|
||||
-C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s \
|
||||
-L../libcompiler-rt
|
||||
|
||||
ksupport.elf: $(OBJECTS_KSUPPORT) $(RUSTOUT_DIRECTORY)/libksupport.a
|
||||
$(LD) $(LDFLAGS) \
|
||||
--eh-frame-hdr \
|
||||
-T $(RUNTIME_DIRECTORY)/ksupport.ld \
|
||||
-o $@ \
|
||||
$^ \
|
||||
-lbase -lm -lcompiler-rt -ldyld -lunwind
|
||||
@chmod -x $@
|
||||
|
||||
%.o: $(RUNTIME_DIRECTORY)/%.c
|
||||
$(compile)
|
||||
|
||||
%.bin: %.elf
|
||||
$(OBJCOPY) -O binary $< $@
|
||||
@chmod -x $@
|
||||
@ -27,56 +79,10 @@ all: runtime.bin runtime.fbi
|
||||
%.fbi: %.bin
|
||||
@echo " MSCIMG " $@ && $(PYTHON) -m misoc.tools.mkmscimg -f -o $@ $<
|
||||
|
||||
runtime.elf: $(OBJECTS) libartiq_rust.a
|
||||
$(LD) $(LDFLAGS) \
|
||||
--gc-sections \
|
||||
-T $(RUNTIME_DIRECTORY)/runtime.ld \
|
||||
-N -o $@ \
|
||||
../libbase/crt0-$(CPU).o \
|
||||
$(OBJECTS) \
|
||||
-L../libcompiler-rt \
|
||||
-L../libbase \
|
||||
-L../libm \
|
||||
-L../liballoc \
|
||||
-L../liblwip \
|
||||
-Lcargo/or1k-unknown-none/debug/ \
|
||||
-lartiq_rust -lbase -lm -lcompiler-rt -lalloc -llwip
|
||||
@chmod -x $@
|
||||
|
||||
ksupport.elf: $(OBJECTS_KSUPPORT)
|
||||
$(LD) $(LDFLAGS) \
|
||||
--eh-frame-hdr \
|
||||
-T $(RUNTIME_DIRECTORY)/ksupport.ld \
|
||||
-N -o $@ \
|
||||
../libbase/crt0-$(CPU).o \
|
||||
$^ \
|
||||
-L../libbase \
|
||||
-L../libcompiler-rt \
|
||||
-L../libunwind \
|
||||
-L../libdyld \
|
||||
-lbase-nofloat -lcompiler-rt -ldyld -lunwind
|
||||
@chmod -x $@
|
||||
|
||||
ksupport_data.o: ksupport.elf
|
||||
$(LD) -r -b binary -o $@ $<
|
||||
|
||||
libartiq_rust.a:
|
||||
CARGO_TARGET_DIR="./cargo" \
|
||||
cargo rustc --verbose \
|
||||
--manifest-path $(RUNTIME_DIRECTORY)/../runtime.rs/Cargo.toml \
|
||||
--target=or1k-unknown-none -- \
|
||||
-C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s \
|
||||
-L../libcompiler-rt
|
||||
|
||||
%.o: $(RUNTIME_DIRECTORY)/%.c
|
||||
$(compile)
|
||||
|
||||
%.o: $(RUNTIME_DIRECTORY)/%.S
|
||||
$(assemble)
|
||||
|
||||
clean:
|
||||
$(RM) $(OBJECTS) $(OBJECTS_KSUPPORT)
|
||||
$(RM) runtime.elf runtime.bin runtime.fbi .*~ *~
|
||||
$(RM) ksupport.elf ksupport.bin
|
||||
$(RM) -rf cargo
|
||||
|
||||
.PHONY: all clean
|
||||
|
@ -1,158 +0,0 @@
|
||||
#include <system.h>
|
||||
#include <generated/csr.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "analyzer.h"
|
||||
|
||||
|
||||
#ifdef CSR_RTIO_ANALYZER_BASE
|
||||
|
||||
struct analyzer_header {
|
||||
unsigned int sent_bytes;
|
||||
unsigned long long int total_byte_count;
|
||||
unsigned char overflow_occured;
|
||||
unsigned char log_channel;
|
||||
unsigned char dds_onehot_sel;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
#define ANALYZER_BUFFER_SIZE (512*1024)
|
||||
|
||||
static struct analyzer_header analyzer_header;
|
||||
static char analyzer_buffer[ANALYZER_BUFFER_SIZE] __attribute__((aligned(64)));
|
||||
|
||||
static void arm(void)
|
||||
{
|
||||
rtio_analyzer_message_encoder_overflow_reset_write(1);
|
||||
rtio_analyzer_dma_base_address_write((unsigned int)analyzer_buffer);
|
||||
rtio_analyzer_dma_last_address_write((unsigned int)analyzer_buffer + ANALYZER_BUFFER_SIZE - 1);
|
||||
rtio_analyzer_dma_reset_write(1);
|
||||
rtio_analyzer_enable_write(1);
|
||||
}
|
||||
|
||||
static void disarm(void)
|
||||
{
|
||||
rtio_analyzer_enable_write(0);
|
||||
while(rtio_analyzer_busy_read());
|
||||
flush_cpu_dcache();
|
||||
flush_l2_cache();
|
||||
}
|
||||
|
||||
void analyzer_init(void)
|
||||
{
|
||||
arm();
|
||||
}
|
||||
|
||||
enum {
|
||||
SEND_STATE_HEADER,
|
||||
SEND_STATE_POST_POINTER, /* send from pointer to end of buffer */
|
||||
SEND_STATE_PRE_POINTER, /* send from start of buffer to pointer-1 */
|
||||
SEND_STATE_TERMINATE
|
||||
};
|
||||
|
||||
static int send_state;
|
||||
static int pointer;
|
||||
static int wraparound;
|
||||
static int offset_consumed;
|
||||
static int offset_sent;
|
||||
|
||||
void analyzer_start(void)
|
||||
{
|
||||
disarm();
|
||||
|
||||
analyzer_header.total_byte_count = rtio_analyzer_dma_byte_count_read();
|
||||
pointer = analyzer_header.total_byte_count % ANALYZER_BUFFER_SIZE;
|
||||
wraparound = analyzer_header.total_byte_count >= ANALYZER_BUFFER_SIZE;
|
||||
|
||||
if(wraparound)
|
||||
analyzer_header.sent_bytes = ANALYZER_BUFFER_SIZE;
|
||||
else
|
||||
analyzer_header.sent_bytes = analyzer_header.total_byte_count;
|
||||
|
||||
analyzer_header.overflow_occured = rtio_analyzer_message_encoder_overflow_read();
|
||||
analyzer_header.log_channel = CONFIG_RTIO_LOG_CHANNEL;
|
||||
#ifdef CONFIG_DDS_ONEHOT_SEL
|
||||
analyzer_header.dds_onehot_sel = 1;
|
||||
#else
|
||||
analyzer_header.dds_onehot_sel = 0;
|
||||
#endif
|
||||
|
||||
offset_consumed = 0;
|
||||
offset_sent = 0;
|
||||
send_state = SEND_STATE_HEADER;
|
||||
}
|
||||
|
||||
void analyzer_end(void)
|
||||
{
|
||||
arm();
|
||||
}
|
||||
|
||||
int analyzer_input(void *data, int length)
|
||||
{
|
||||
core_log("no input should be received by analyzer, dropping connection\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void analyzer_poll(void **data, int *length, int *close_flag)
|
||||
{
|
||||
*close_flag = 0;
|
||||
switch(send_state) {
|
||||
case SEND_STATE_HEADER:
|
||||
*length = sizeof(struct analyzer_header) - offset_consumed;
|
||||
*data = (char *)&analyzer_header + offset_consumed;
|
||||
break;
|
||||
case SEND_STATE_POST_POINTER:
|
||||
*length = ANALYZER_BUFFER_SIZE - pointer - offset_consumed;
|
||||
*data = analyzer_buffer + pointer + offset_consumed;
|
||||
break;
|
||||
case SEND_STATE_PRE_POINTER:
|
||||
*length = pointer - offset_consumed;
|
||||
*data = analyzer_buffer + offset_consumed;
|
||||
break;
|
||||
case SEND_STATE_TERMINATE:
|
||||
*length = -1;
|
||||
break;
|
||||
default:
|
||||
*length = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void analyzer_ack_consumed(int length)
|
||||
{
|
||||
offset_consumed += length;
|
||||
}
|
||||
|
||||
void analyzer_ack_sent(int length)
|
||||
{
|
||||
offset_sent += length;
|
||||
switch(send_state) {
|
||||
case SEND_STATE_HEADER:
|
||||
if(offset_sent >= sizeof(struct analyzer_header)) {
|
||||
offset_consumed = 0;
|
||||
offset_sent = 0;
|
||||
if(wraparound)
|
||||
send_state = SEND_STATE_POST_POINTER;
|
||||
else {
|
||||
if(pointer)
|
||||
send_state = SEND_STATE_PRE_POINTER;
|
||||
else
|
||||
send_state = SEND_STATE_TERMINATE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SEND_STATE_POST_POINTER:
|
||||
if(pointer + offset_consumed >= ANALYZER_BUFFER_SIZE) {
|
||||
offset_consumed = 0;
|
||||
offset_sent = 0;
|
||||
send_state = SEND_STATE_PRE_POINTER;
|
||||
}
|
||||
break;
|
||||
case SEND_STATE_PRE_POINTER:
|
||||
if(offset_sent >= pointer)
|
||||
send_state = SEND_STATE_TERMINATE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,14 +0,0 @@
|
||||
#ifndef __ANALYZER_H
|
||||
#define __ANALYZER_H
|
||||
|
||||
void analyzer_init(void);
|
||||
|
||||
void analyzer_start(void);
|
||||
void analyzer_end(void);
|
||||
|
||||
int analyzer_input(void *data, int length);
|
||||
void analyzer_poll(void **data, int *length, int *close_flag);
|
||||
void analyzer_ack_consumed(int length);
|
||||
void analyzer_ack_sent(int length);
|
||||
|
||||
#endif /* __ANALYZER_H */
|
@ -233,7 +233,7 @@ struct artiq_raised_exception {
|
||||
struct _Unwind_Exception unwind;
|
||||
struct artiq_exception artiq;
|
||||
int handled;
|
||||
struct artiq_backtrace_item backtrace[1024];
|
||||
uintptr_t backtrace[1024];
|
||||
size_t backtrace_size;
|
||||
};
|
||||
|
||||
@ -303,8 +303,7 @@ static _Unwind_Reason_Code __artiq_uncaught_exception(
|
||||
uintptr_t pcOffset = pc - funcStart;
|
||||
EH_LOG("===> uncaught (pc=%p+%p)", (void*)funcStart, (void*)pcOffset);
|
||||
|
||||
inflight->backtrace[inflight->backtrace_size].function = funcStart;
|
||||
inflight->backtrace[inflight->backtrace_size].offset = pcOffset;
|
||||
inflight->backtrace[inflight->backtrace_size] = funcStart + pcOffset;
|
||||
++inflight->backtrace_size;
|
||||
|
||||
if(actions & _UA_END_OF_STACK) {
|
||||
|
@ -17,11 +17,6 @@ struct artiq_exception {
|
||||
int64_t param[3];
|
||||
};
|
||||
|
||||
struct artiq_backtrace_item {
|
||||
intptr_t function;
|
||||
intptr_t offset;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -48,7 +43,7 @@ void __artiq_reraise(void)
|
||||
|
||||
/* Called by the runtime */
|
||||
void __artiq_terminate(struct artiq_exception *artiq_exn,
|
||||
struct artiq_backtrace_item *backtrace,
|
||||
uintptr_t *backtrace,
|
||||
size_t backtrace_size)
|
||||
__attribute__((noreturn));
|
||||
|
||||
|
@ -1,131 +0,0 @@
|
||||
#include "mailbox.h"
|
||||
#include "messages.h"
|
||||
#include "rtio.h"
|
||||
#include "ttl.h"
|
||||
#include "dds.h"
|
||||
#include "bridge.h"
|
||||
|
||||
#define TIME_BUFFER (8000 << CONFIG_RTIO_FINE_TS_WIDTH)
|
||||
|
||||
static void rtio_output_blind(int channel, int addr, int data)
|
||||
{
|
||||
rtio_chan_sel_write(channel);
|
||||
#ifdef CSR_RTIO_O_ADDRESS_ADDR
|
||||
rtio_o_address_write(addr);
|
||||
#endif
|
||||
rtio_o_data_write(data);
|
||||
rtio_o_timestamp_write(rtio_get_counter() + TIME_BUFFER);
|
||||
rtio_o_we_write(1);
|
||||
}
|
||||
|
||||
#if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0))
|
||||
static void dds_write(int bus_channel, int addr, int data)
|
||||
{
|
||||
rtio_output_blind(bus_channel, addr, data);
|
||||
}
|
||||
|
||||
static int dds_read(int bus_channel, int addr)
|
||||
{
|
||||
int r;
|
||||
|
||||
#ifdef CONFIG_DDS_AD9858
|
||||
#define DDS_READ_FLAG 128
|
||||
#endif
|
||||
#ifdef CONFIG_DDS_AD9914
|
||||
#define DDS_READ_FLAG 256
|
||||
#endif
|
||||
dds_write(bus_channel, addr | DDS_READ_FLAG, 0);
|
||||
while(rtio_i_status_read() & RTIO_I_STATUS_EMPTY);
|
||||
r = rtio_i_data_read();
|
||||
rtio_i_re_write(1);
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void send_ready(void)
|
||||
{
|
||||
struct msg_base msg;
|
||||
|
||||
msg.type = MESSAGE_TYPE_BRG_READY;
|
||||
mailbox_send_and_wait(&msg);
|
||||
}
|
||||
|
||||
void bridge_main(void)
|
||||
{
|
||||
struct msg_base *umsg;
|
||||
|
||||
rtio_init();
|
||||
send_ready();
|
||||
while(1) {
|
||||
umsg = mailbox_wait_and_receive();
|
||||
switch(umsg->type) {
|
||||
case MESSAGE_TYPE_BRG_TTL_OE: {
|
||||
struct msg_brg_ttl_out *msg;
|
||||
|
||||
msg = (struct msg_brg_ttl_out *)umsg;
|
||||
rtio_output_blind(msg->channel, TTL_OE_ADDR, msg->value);
|
||||
mailbox_acknowledge();
|
||||
break;
|
||||
}
|
||||
case MESSAGE_TYPE_BRG_TTL_O: {
|
||||
struct msg_brg_ttl_out *msg;
|
||||
|
||||
msg = (struct msg_brg_ttl_out *)umsg;
|
||||
rtio_output_blind(msg->channel, TTL_O_ADDR, msg->value);
|
||||
mailbox_acknowledge();
|
||||
break;
|
||||
}
|
||||
#if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0))
|
||||
case MESSAGE_TYPE_BRG_DDS_SEL: {
|
||||
struct msg_brg_dds_sel *msg;
|
||||
|
||||
msg = (struct msg_brg_dds_sel *)umsg;
|
||||
dds_write(msg->bus_channel, DDS_GPIO, msg->channel << 1);
|
||||
mailbox_acknowledge();
|
||||
break;
|
||||
}
|
||||
case MESSAGE_TYPE_BRG_DDS_RESET: {
|
||||
unsigned int g;
|
||||
struct msg_brg_dds_reset *msg;
|
||||
|
||||
msg = (struct msg_brg_dds_reset *)umsg;
|
||||
g = dds_read(msg->bus_channel, DDS_GPIO);
|
||||
dds_write(msg->bus_channel, DDS_GPIO, g | 1);
|
||||
dds_write(msg->bus_channel, DDS_GPIO, g);
|
||||
|
||||
mailbox_acknowledge();
|
||||
break;
|
||||
}
|
||||
case MESSAGE_TYPE_BRG_DDS_READ_REQUEST: {
|
||||
struct msg_brg_dds_read_request *msg;
|
||||
struct msg_brg_dds_read_reply rmsg;
|
||||
|
||||
msg = (struct msg_brg_dds_read_request *)umsg;
|
||||
rmsg.type = MESSAGE_TYPE_BRG_DDS_READ_REPLY;
|
||||
rmsg.data = dds_read(msg->bus_channel, msg->address);
|
||||
mailbox_send_and_wait(&rmsg);
|
||||
break;
|
||||
}
|
||||
case MESSAGE_TYPE_BRG_DDS_WRITE: {
|
||||
struct msg_brg_dds_write *msg;
|
||||
|
||||
msg = (struct msg_brg_dds_write *)umsg;
|
||||
dds_write(msg->bus_channel, msg->address, msg->data);
|
||||
mailbox_acknowledge();
|
||||
break;
|
||||
}
|
||||
case MESSAGE_TYPE_BRG_DDS_FUD: {
|
||||
struct msg_brg_dds_fud *msg;
|
||||
|
||||
msg = (struct msg_brg_dds_fud *)umsg;
|
||||
dds_write(msg->bus_channel, DDS_FUD, 0);
|
||||
mailbox_acknowledge();
|
||||
break;
|
||||
}
|
||||
#endif /* CONFIG_RTIO_DDS_COUNT */
|
||||
default:
|
||||
mailbox_acknowledge();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
#ifndef __BRIDGE_H
|
||||
#define __BRIDGE_H
|
||||
|
||||
void bridge_main(void);
|
||||
|
||||
#endif /* __BRIDGE_H */
|
@ -1,106 +0,0 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "kloader.h"
|
||||
#include "mailbox.h"
|
||||
#include "messages.h"
|
||||
#include "bridge_ctl.h"
|
||||
|
||||
void brg_start(void)
|
||||
{
|
||||
struct msg_base *umsg;
|
||||
|
||||
kloader_start_bridge();
|
||||
|
||||
while(1) {
|
||||
umsg = mailbox_wait_and_receive();
|
||||
if(umsg->type == MESSAGE_TYPE_BRG_READY) {
|
||||
mailbox_acknowledge();
|
||||
break;
|
||||
} else {
|
||||
printf("Warning: unexpected message %d from AMP bridge\n", umsg->type);
|
||||
mailbox_acknowledge();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void brg_ttloe(int n, int value)
|
||||
{
|
||||
struct msg_brg_ttl_out msg;
|
||||
|
||||
msg.type = MESSAGE_TYPE_BRG_TTL_OE;
|
||||
msg.channel = n;
|
||||
msg.value = value;
|
||||
mailbox_send_and_wait(&msg);
|
||||
}
|
||||
|
||||
void brg_ttlo(int n, int value)
|
||||
{
|
||||
struct msg_brg_ttl_out msg;
|
||||
|
||||
msg.type = MESSAGE_TYPE_BRG_TTL_O;
|
||||
msg.channel = n;
|
||||
msg.value = value;
|
||||
mailbox_send_and_wait(&msg);
|
||||
}
|
||||
|
||||
void brg_ddssel(int bus_channel, int channel)
|
||||
{
|
||||
struct msg_brg_dds_sel msg;
|
||||
|
||||
msg.type = MESSAGE_TYPE_BRG_DDS_SEL;
|
||||
msg.bus_channel = bus_channel;
|
||||
msg.channel = channel;
|
||||
mailbox_send_and_wait(&msg);
|
||||
}
|
||||
|
||||
void brg_ddsreset(int bus_channel)
|
||||
{
|
||||
struct msg_brg_dds_reset msg;
|
||||
|
||||
msg.type = MESSAGE_TYPE_BRG_DDS_RESET;
|
||||
msg.bus_channel = bus_channel;
|
||||
mailbox_send_and_wait(&msg);
|
||||
}
|
||||
|
||||
unsigned int brg_ddsread(int bus_channel, unsigned int address)
|
||||
{
|
||||
struct msg_brg_dds_read_request msg;
|
||||
struct msg_brg_dds_read_reply *rmsg;
|
||||
unsigned int r;
|
||||
|
||||
msg.type = MESSAGE_TYPE_BRG_DDS_READ_REQUEST;
|
||||
msg.bus_channel = bus_channel;
|
||||
msg.address = address;
|
||||
mailbox_send(&msg);
|
||||
while(1) {
|
||||
rmsg = mailbox_wait_and_receive();
|
||||
if(rmsg->type == MESSAGE_TYPE_BRG_DDS_READ_REPLY) {
|
||||
r = rmsg->data;
|
||||
mailbox_acknowledge();
|
||||
return r;
|
||||
} else {
|
||||
printf("Warning: unexpected message %d from AMP bridge\n", rmsg->type);
|
||||
mailbox_acknowledge();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void brg_ddswrite(int bus_channel, unsigned int address, unsigned int data)
|
||||
{
|
||||
struct msg_brg_dds_write msg;
|
||||
|
||||
msg.type = MESSAGE_TYPE_BRG_DDS_WRITE;
|
||||
msg.bus_channel = bus_channel;
|
||||
msg.address = address;
|
||||
msg.data = data;
|
||||
mailbox_send_and_wait(&msg);
|
||||
}
|
||||
|
||||
void brg_ddsfud(int bus_channel)
|
||||
{
|
||||
struct msg_brg_dds_fud msg;
|
||||
|
||||
msg.type = MESSAGE_TYPE_BRG_DDS_FUD;
|
||||
msg.bus_channel = bus_channel;
|
||||
mailbox_send_and_wait(&msg);
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
#ifndef __BRIDGE_CTL_H
|
||||
#define __BRIDGE_CTL_H
|
||||
|
||||
void brg_start(void);
|
||||
|
||||
void brg_ttloe(int n, int value);
|
||||
void brg_ttlo(int n, int value);
|
||||
|
||||
void brg_ddssel(int bus_channel, int channel);
|
||||
void brg_ddsreset(int bus_channel);
|
||||
unsigned int brg_ddsread(int bus_channel, unsigned int address);
|
||||
void brg_ddswrite(int bus_channel, unsigned int address, unsigned int data);
|
||||
void brg_ddsfud(int bus_channel);
|
||||
|
||||
#endif /* __BRIDGE_CTL_H */
|
@ -1,89 +0,0 @@
|
||||
#include <generated/csr.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "clock.h"
|
||||
|
||||
|
||||
void clock_init(void)
|
||||
{
|
||||
timer0_en_write(0);
|
||||
timer0_load_write(0x7fffffffffffffffLL);
|
||||
timer0_reload_write(0x7fffffffffffffffLL);
|
||||
timer0_en_write(1);
|
||||
}
|
||||
|
||||
long long int clock_get_ms(void)
|
||||
{
|
||||
long long int clock_sys;
|
||||
long long int clock_ms;
|
||||
|
||||
timer0_update_value_write(1);
|
||||
clock_sys = 0x7fffffffffffffffLL - timer0_value_read();
|
||||
|
||||
clock_ms = clock_sys/(CONFIG_CLOCK_FREQUENCY/1000);
|
||||
return clock_ms;
|
||||
}
|
||||
|
||||
void busywait_us(long long int us)
|
||||
{
|
||||
long long int threshold;
|
||||
|
||||
timer0_update_value_write(1);
|
||||
threshold = timer0_value_read() - us*CONFIG_CLOCK_FREQUENCY/1000000LL;
|
||||
while(timer0_value_read() > threshold)
|
||||
timer0_update_value_write(1);
|
||||
}
|
||||
|
||||
struct watchdog {
|
||||
int active;
|
||||
long long int threshold;
|
||||
};
|
||||
|
||||
static struct watchdog watchdogs[MAX_WATCHDOGS];
|
||||
|
||||
void watchdog_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0;i<MAX_WATCHDOGS;i++)
|
||||
watchdogs[i].active = 0;
|
||||
}
|
||||
|
||||
int watchdog_set(int ms)
|
||||
{
|
||||
int i, id;
|
||||
|
||||
id = -1;
|
||||
for(i=0;i<MAX_WATCHDOGS;i++)
|
||||
if(!watchdogs[i].active) {
|
||||
id = i;
|
||||
break;
|
||||
}
|
||||
if(id < 0) {
|
||||
core_log("WARNING: Failed to add watchdog\n");
|
||||
return id;
|
||||
}
|
||||
|
||||
watchdogs[id].active = 1;
|
||||
watchdogs[id].threshold = clock_get_ms() + ms;
|
||||
return id;
|
||||
}
|
||||
|
||||
void watchdog_clear(int id)
|
||||
{
|
||||
if((id < 0) || (id >= MAX_WATCHDOGS))
|
||||
return;
|
||||
watchdogs[id].active = 0;
|
||||
}
|
||||
|
||||
int watchdog_expired(void)
|
||||
{
|
||||
int i;
|
||||
long long int t;
|
||||
|
||||
t = 0x7fffffffffffffffLL;
|
||||
for(i=0;i<MAX_WATCHDOGS;i++)
|
||||
if(watchdogs[i].active && (watchdogs[i].threshold < t))
|
||||
t = watchdogs[i].threshold;
|
||||
return clock_get_ms() > t;
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
#ifndef __CLOCK_H
|
||||
#define __CLOCK_H
|
||||
|
||||
void clock_init(void);
|
||||
long long int clock_get_ms(void);
|
||||
void busywait_us(long long us);
|
||||
|
||||
#define MAX_WATCHDOGS 16
|
||||
|
||||
void watchdog_init(void);
|
||||
int watchdog_set(int ms);
|
||||
void watchdog_clear(int id);
|
||||
int watchdog_expired(void);
|
||||
|
||||
#endif /* __CLOCK_H */
|
@ -6,7 +6,6 @@
|
||||
|
||||
#include "artiq_personality.h"
|
||||
#include "rtio.h"
|
||||
#include "log.h"
|
||||
#include "dds.h"
|
||||
|
||||
#define DURATION_WRITE (5 << CONFIG_RTIO_FINE_TS_WIDTH)
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include <generated/mem.h>
|
||||
#include <generated/csr.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "flash_storage.h"
|
||||
|
||||
#if (defined CSR_SPIFLASH_BASE && defined CONFIG_SPIFLASH_PAGE_SIZE)
|
||||
@ -63,24 +62,24 @@ static int record_iter_next(struct iter_state *is, struct record *record, int *f
|
||||
return 0;
|
||||
|
||||
if(record->size < 6) {
|
||||
core_log("flash_storage might be corrupted: record size is %u (<6) at address %08x\n",
|
||||
record->size, record->raw_record);
|
||||
// core_log("flash_storage might be corrupted: record size is %u (<6) at address %08x\n",
|
||||
// record->size, record->raw_record);
|
||||
if(fatal)
|
||||
*fatal = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(is->seek > is->buf_len - sizeof(record->size) - 2) { /* 2 is the minimum key length */
|
||||
core_log("flash_storage might be corrupted: END_MARKER missing at the end of "
|
||||
"the storage sector\n");
|
||||
// core_log("flash_storage might be corrupted: END_MARKER missing at the end of "
|
||||
// "the storage sector\n");
|
||||
if(fatal)
|
||||
*fatal = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(record->size > is->buf_len - is->seek) {
|
||||
core_log("flash_storage might be corrupted: invalid record_size %d at address %08x\n",
|
||||
record->size, record->raw_record);
|
||||
// core_log("flash_storage might be corrupted: invalid record_size %d at address %08x\n",
|
||||
// record->size, record->raw_record);
|
||||
if(fatal)
|
||||
*fatal = 1;
|
||||
return 0;
|
||||
@ -90,8 +89,8 @@ static int record_iter_next(struct iter_state *is, struct record *record, int *f
|
||||
record->key_len = strnlen(record->key, record->size - sizeof(record->size)) + 1;
|
||||
|
||||
if(record->key_len == record->size - sizeof(record->size) + 1) {
|
||||
core_log("flash_storage might be corrupted: invalid key length at address %08x\n",
|
||||
record->raw_record);
|
||||
// core_log("flash_storage might be corrupted: invalid key length at address %08x\n",
|
||||
// record->raw_record);
|
||||
if(fatal)
|
||||
*fatal = 1;
|
||||
return 0;
|
||||
@ -265,7 +264,7 @@ int fs_write(const char *key, const void *buffer, unsigned int buf_len)
|
||||
return 0; // Storage is definitely full.
|
||||
|
||||
fatal_error:
|
||||
core_log("fatal error: flash storage might be corrupted\n");
|
||||
// core_log("fatal error: flash storage might be corrupted\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -295,8 +294,8 @@ unsigned int fs_read(const char *key, void *buffer, unsigned int buf_len, unsign
|
||||
}
|
||||
}
|
||||
|
||||
if(fatal)
|
||||
core_log("fatal error: flash storage might be corrupted\n");
|
||||
// if(fatal)
|
||||
// core_log("fatal error: flash storage might be corrupted\n");
|
||||
|
||||
return read_length;
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
#include <generated/csr.h>
|
||||
#include <irq.h>
|
||||
#include <uart.h>
|
||||
|
||||
void isr(void);
|
||||
void isr(void)
|
||||
{
|
||||
unsigned int irqs;
|
||||
|
||||
irqs = irq_pending() & irq_getmask();
|
||||
|
||||
if(irqs & (1 << UART_INTERRUPT))
|
||||
uart_isr();
|
||||
}
|
@ -1,206 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <generated/csr.h>
|
||||
|
||||
#include <dyld.h>
|
||||
|
||||
#include "kloader.h"
|
||||
#include "log.h"
|
||||
#include "clock.h"
|
||||
#include "flash_storage.h"
|
||||
#include "mailbox.h"
|
||||
#include "messages.h"
|
||||
|
||||
static void start_kernel_cpu(struct msg_load_request *msg)
|
||||
{
|
||||
// Stop kernel CPU before messing with its code.
|
||||
kernel_cpu_reset_write(1);
|
||||
|
||||
// Load kernel support code.
|
||||
extern void _binary_ksupport_elf_start, _binary_ksupport_elf_end;
|
||||
memcpy((void *)(KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE),
|
||||
&_binary_ksupport_elf_start,
|
||||
&_binary_ksupport_elf_end - &_binary_ksupport_elf_start);
|
||||
|
||||
// Start kernel CPU.
|
||||
mailbox_send(msg);
|
||||
kernel_cpu_reset_write(0);
|
||||
}
|
||||
|
||||
void kloader_start_bridge()
|
||||
{
|
||||
start_kernel_cpu(NULL);
|
||||
}
|
||||
|
||||
static int load_or_start_kernel(const void *library, int run_kernel)
|
||||
{
|
||||
static struct dyld_info library_info;
|
||||
struct msg_load_request request = {
|
||||
.library = library,
|
||||
.library_info = &library_info,
|
||||
.run_kernel = run_kernel,
|
||||
};
|
||||
start_kernel_cpu(&request);
|
||||
|
||||
struct msg_load_reply *reply = mailbox_wait_and_receive();
|
||||
mailbox_acknowledge();
|
||||
|
||||
if(reply->type != MESSAGE_TYPE_LOAD_REPLY) {
|
||||
core_log("BUG: unexpected reply to load/run request\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(reply->error != NULL) {
|
||||
core_log("cannot load kernel: %s\n", reply->error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int kloader_load_library(const void *library)
|
||||
{
|
||||
if(!kernel_cpu_reset_read()) {
|
||||
core_log("BUG: attempted to load kernel library while kernel CPU is running\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return load_or_start_kernel(library, 0);
|
||||
}
|
||||
|
||||
void kloader_filter_backtrace(struct artiq_backtrace_item *backtrace,
|
||||
size_t *backtrace_size) {
|
||||
struct artiq_backtrace_item *cursor = backtrace;
|
||||
|
||||
// Remove all backtrace items belonging to ksupport and subtract
|
||||
// shared object base from the addresses.
|
||||
for(int i = 0; i < *backtrace_size; i++) {
|
||||
if(backtrace[i].function > KERNELCPU_PAYLOAD_ADDRESS) {
|
||||
backtrace[i].function -= KERNELCPU_PAYLOAD_ADDRESS;
|
||||
*cursor++ = backtrace[i];
|
||||
}
|
||||
}
|
||||
|
||||
*backtrace_size = cursor - backtrace;
|
||||
}
|
||||
|
||||
void kloader_start_kernel()
|
||||
{
|
||||
load_or_start_kernel(NULL, 1);
|
||||
}
|
||||
|
||||
static int kloader_start_flash_kernel(char *key)
|
||||
{
|
||||
#if (defined CSR_SPIFLASH_BASE && defined CONFIG_SPIFLASH_PAGE_SIZE)
|
||||
char buffer[32*1024];
|
||||
unsigned int length, remain;
|
||||
|
||||
length = fs_read(key, buffer, sizeof(buffer), &remain);
|
||||
if(length <= 0)
|
||||
return 0;
|
||||
|
||||
if(remain) {
|
||||
core_log("ERROR: kernel %s is too large\n", key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return load_or_start_kernel(buffer, 1);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int kloader_start_startup_kernel(void)
|
||||
{
|
||||
return kloader_start_flash_kernel("startup_kernel");
|
||||
}
|
||||
|
||||
int kloader_start_idle_kernel(void)
|
||||
{
|
||||
return kloader_start_flash_kernel("idle_kernel");
|
||||
}
|
||||
|
||||
void kloader_stop(void)
|
||||
{
|
||||
kernel_cpu_reset_write(1);
|
||||
mailbox_acknowledge();
|
||||
}
|
||||
|
||||
int kloader_validate_kpointer(void *p)
|
||||
{
|
||||
unsigned int v = (unsigned int)p;
|
||||
if((v < KERNELCPU_EXEC_ADDRESS) || (v > KERNELCPU_LAST_ADDRESS)) {
|
||||
core_log("Received invalid pointer from kernel CPU: 0x%08x\n", v);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int kloader_is_essential_kmsg(int msgtype)
|
||||
{
|
||||
switch(msgtype) {
|
||||
case MESSAGE_TYPE_NOW_INIT_REQUEST:
|
||||
case MESSAGE_TYPE_NOW_SAVE:
|
||||
case MESSAGE_TYPE_LOG:
|
||||
case MESSAGE_TYPE_WATCHDOG_SET_REQUEST:
|
||||
case MESSAGE_TYPE_WATCHDOG_CLEAR:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static long long int now = 0;
|
||||
|
||||
void kloader_service_essential_kmsg(void)
|
||||
{
|
||||
struct msg_base *umsg;
|
||||
|
||||
umsg = mailbox_receive();
|
||||
if(umsg) {
|
||||
if(!kloader_validate_kpointer(umsg))
|
||||
return;
|
||||
switch(umsg->type) {
|
||||
case MESSAGE_TYPE_NOW_INIT_REQUEST: {
|
||||
struct msg_now_init_reply reply;
|
||||
|
||||
reply.type = MESSAGE_TYPE_NOW_INIT_REPLY;
|
||||
reply.now = now;
|
||||
mailbox_send_and_wait(&reply);
|
||||
break;
|
||||
}
|
||||
case MESSAGE_TYPE_NOW_SAVE: {
|
||||
struct msg_now_save *msg = (struct msg_now_save *)umsg;
|
||||
|
||||
now = msg->now;
|
||||
mailbox_acknowledge();
|
||||
break;
|
||||
}
|
||||
case MESSAGE_TYPE_LOG: {
|
||||
struct msg_log *msg = (struct msg_log *)umsg;
|
||||
|
||||
core_log_va(msg->fmt, msg->args);
|
||||
mailbox_acknowledge();
|
||||
break;
|
||||
}
|
||||
case MESSAGE_TYPE_WATCHDOG_SET_REQUEST: {
|
||||
struct msg_watchdog_set_request *msg = (struct msg_watchdog_set_request *)umsg;
|
||||
struct msg_watchdog_set_reply reply;
|
||||
|
||||
reply.type = MESSAGE_TYPE_WATCHDOG_SET_REPLY;
|
||||
reply.id = watchdog_set(msg->ms);
|
||||
mailbox_send_and_wait(&reply);
|
||||
break;
|
||||
}
|
||||
case MESSAGE_TYPE_WATCHDOG_CLEAR: {
|
||||
struct msg_watchdog_clear *msg = (struct msg_watchdog_clear *)umsg;
|
||||
|
||||
watchdog_clear(msg->id);
|
||||
mailbox_acknowledge();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* handled elsewhere */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
#ifndef __KLOADER_H
|
||||
#define __KLOADER_H
|
||||
|
||||
#include "artiq_personality.h"
|
||||
|
||||
#define KERNELCPU_EXEC_ADDRESS 0x42000000
|
||||
#define KERNELCPU_PAYLOAD_ADDRESS 0x42020000
|
||||
#define KERNELCPU_LAST_ADDRESS (0x4fffffff - 1024*1024)
|
||||
#define KSUPPORT_HEADER_SIZE 0x80
|
||||
|
||||
int kloader_load_library(const void *code);
|
||||
void kloader_filter_backtrace(struct artiq_backtrace_item *backtrace,
|
||||
size_t *backtrace_size);
|
||||
|
||||
void kloader_start_bridge(void);
|
||||
int kloader_start_startup_kernel(void);
|
||||
int kloader_start_idle_kernel(void);
|
||||
void kloader_start_kernel(void);
|
||||
void kloader_stop(void);
|
||||
|
||||
int kloader_validate_kpointer(void *p);
|
||||
int kloader_is_essential_kmsg(int msgtype);
|
||||
void kloader_service_essential_kmsg(void);
|
||||
|
||||
#endif /* __KLOADER_H */
|
@ -1,624 +0,0 @@
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <generated/csr.h>
|
||||
|
||||
#include <link.h>
|
||||
#include <dlfcn.h>
|
||||
#include <dyld.h>
|
||||
#include <unwind.h>
|
||||
|
||||
#include "ksupport.h"
|
||||
#include "kloader.h"
|
||||
#include "mailbox.h"
|
||||
#include "messages.h"
|
||||
#include "bridge.h"
|
||||
#include "artiq_personality.h"
|
||||
#include "rtio.h"
|
||||
#include "dds.h"
|
||||
#include "i2c.h"
|
||||
#include "ad9154.h"
|
||||
|
||||
double round(double x);
|
||||
double sqrt(double x);
|
||||
|
||||
void ksupport_abort(void);
|
||||
static void attribute_writeback(void *);
|
||||
|
||||
int64_t now;
|
||||
|
||||
/* compiler-rt symbols */
|
||||
extern void __divsi3, __modsi3, __ledf2, __gedf2, __unorddf2, __eqdf2, __ltdf2,
|
||||
__nedf2, __gtdf2, __negsf2, __negdf2, __addsf3, __subsf3, __mulsf3,
|
||||
__divsf3, __lshrdi3, __muldi3, __divdi3, __ashldi3, __ashrdi3,
|
||||
__udivmoddi4, __floatsisf, __floatunsisf, __fixsfsi, __fixunssfsi,
|
||||
__adddf3, __subdf3, __muldf3, __divdf3, __floatsidf, __floatunsidf,
|
||||
__floatdidf, __fixdfsi, __fixdfdi, __fixunsdfsi, __clzsi2, __ctzsi2,
|
||||
__udivdi3, __umoddi3, __moddi3, __powidf2;
|
||||
|
||||
/* artiq_personality symbols */
|
||||
extern void __artiq_personality;
|
||||
|
||||
struct symbol {
|
||||
const char *name;
|
||||
void *addr;
|
||||
};
|
||||
|
||||
static const struct symbol runtime_exports[] = {
|
||||
/* compiler-rt */
|
||||
{"__divsi3", &__divsi3},
|
||||
{"__modsi3", &__modsi3},
|
||||
{"__ledf2", &__ledf2},
|
||||
{"__gedf2", &__gedf2},
|
||||
{"__unorddf2", &__unorddf2},
|
||||
{"__eqdf2", &__eqdf2},
|
||||
{"__ltdf2", &__ltdf2},
|
||||
{"__nedf2", &__nedf2},
|
||||
{"__gtdf2", &__gtdf2},
|
||||
{"__negsf2", &__negsf2},
|
||||
{"__negdf2", &__negdf2},
|
||||
{"__addsf3", &__addsf3},
|
||||
{"__subsf3", &__subsf3},
|
||||
{"__mulsf3", &__mulsf3},
|
||||
{"__divsf3", &__divsf3},
|
||||
{"__lshrdi3", &__lshrdi3},
|
||||
{"__muldi3", &__muldi3},
|
||||
{"__divdi3", &__divdi3},
|
||||
{"__ashldi3", &__ashldi3},
|
||||
{"__ashrdi3", &__ashrdi3},
|
||||
{"__udivmoddi4", &__udivmoddi4},
|
||||
{"__floatsisf", &__floatsisf},
|
||||
{"__floatunsisf", &__floatunsisf},
|
||||
{"__fixsfsi", &__fixsfsi},
|
||||
{"__fixunssfsi", &__fixunssfsi},
|
||||
{"__adddf3", &__adddf3},
|
||||
{"__subdf3", &__subdf3},
|
||||
{"__muldf3", &__muldf3},
|
||||
{"__divdf3", &__divdf3},
|
||||
{"__floatsidf", &__floatsidf},
|
||||
{"__floatunsidf", &__floatunsidf},
|
||||
{"__floatdidf", &__floatdidf},
|
||||
{"__fixdfsi", &__fixdfsi},
|
||||
{"__fixdfdi", &__fixdfdi},
|
||||
{"__fixunsdfsi", &__fixunsdfsi},
|
||||
{"__clzsi2", &__clzsi2},
|
||||
{"__ctzsi2", &__ctzsi2},
|
||||
{"__udivdi3", &__udivdi3},
|
||||
{"__umoddi3", &__umoddi3},
|
||||
{"__moddi3", &__moddi3},
|
||||
{"__powidf2", &__powidf2},
|
||||
|
||||
/* libm */
|
||||
{"round", &round},
|
||||
{"sqrt", &sqrt},
|
||||
|
||||
/* exceptions */
|
||||
{"_Unwind_Resume", &_Unwind_Resume},
|
||||
{"__artiq_personality", &__artiq_personality},
|
||||
{"__artiq_raise", &__artiq_raise},
|
||||
{"__artiq_reraise", &__artiq_reraise},
|
||||
{"strcmp", &strcmp},
|
||||
{"strlen", &strlen},
|
||||
{"abort", &ksupport_abort},
|
||||
|
||||
/* proxified syscalls */
|
||||
{"core_log", &core_log},
|
||||
|
||||
{"now", &now},
|
||||
|
||||
{"watchdog_set", &watchdog_set},
|
||||
{"watchdog_clear", &watchdog_clear},
|
||||
|
||||
{"printf", &core_log},
|
||||
{"send_rpc", &send_rpc},
|
||||
{"recv_rpc", &recv_rpc},
|
||||
|
||||
/* direct syscalls */
|
||||
{"rtio_init", &rtio_init},
|
||||
{"rtio_get_counter", &rtio_get_counter},
|
||||
{"rtio_log", &rtio_log},
|
||||
{"rtio_output", &rtio_output},
|
||||
{"rtio_input_timestamp", &rtio_input_timestamp},
|
||||
{"rtio_input_data", &rtio_input_data},
|
||||
|
||||
#if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0))
|
||||
{"dds_init", &dds_init},
|
||||
{"dds_init_sync", &dds_init_sync},
|
||||
{"dds_batch_enter", &dds_batch_enter},
|
||||
{"dds_batch_exit", &dds_batch_exit},
|
||||
{"dds_set", &dds_set},
|
||||
#endif
|
||||
|
||||
{"i2c_init", &i2c_init},
|
||||
{"i2c_start", &i2c_start},
|
||||
{"i2c_stop", &i2c_stop},
|
||||
{"i2c_write", &i2c_write},
|
||||
{"i2c_read", &i2c_read},
|
||||
|
||||
{"cache_get", &cache_get},
|
||||
{"cache_put", &cache_put},
|
||||
|
||||
#ifdef CONFIG_AD9154_DAC_CS
|
||||
{"ad9154_init", &ad9154_init},
|
||||
{"ad9154_write", &ad9154_write},
|
||||
{"ad9154_read", &ad9154_read},
|
||||
{"ad9516_write", &ad9516_write},
|
||||
{"ad9516_read", &ad9516_read},
|
||||
{"ad9154_jesd_enable", &ad9154_jesd_enable},
|
||||
{"ad9154_jesd_ready", &ad9154_jesd_ready},
|
||||
{"ad9154_jesd_prbs", &ad9154_jesd_prbs},
|
||||
{"ad9154_jesd_stpl", &ad9154_jesd_stpl},
|
||||
#endif
|
||||
|
||||
/* end */
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
double round(double x)
|
||||
{
|
||||
union {double f; uint64_t i;} u = {x};
|
||||
int e = u.i >> 52 & 0x7ff;
|
||||
double y;
|
||||
|
||||
if (e >= 0x3ff+52)
|
||||
return x;
|
||||
if (u.i >> 63)
|
||||
x = -x;
|
||||
if (e < 0x3ff-1) {
|
||||
/* we don't do it in ARTIQ */
|
||||
/* raise inexact if x!=0 */
|
||||
// FORCE_EVAL(x + 0x1p52);
|
||||
return 0*u.f;
|
||||
}
|
||||
y = (double)(x + 0x1p52) - 0x1p52 - x;
|
||||
if (y > 0.5)
|
||||
y = y + x - 1;
|
||||
else if (y <= -0.5)
|
||||
y = y + x + 1;
|
||||
else
|
||||
y = y + x;
|
||||
if (u.i >> 63)
|
||||
y = -y;
|
||||
return y;
|
||||
}
|
||||
|
||||
double sqrt(double x)
|
||||
{
|
||||
static const double one = 1.0, tiny = 1.0e-300;
|
||||
double z;
|
||||
int32_t sign = (int)0x80000000;
|
||||
int32_t ix0,s0,q,m,t,i;
|
||||
uint32_t r,t1,s1,ix1,q1;
|
||||
|
||||
union {double f; struct{uint32_t msw; uint32_t lsw;};} u = {x};
|
||||
ix0 = u.msw;
|
||||
ix1 = u.lsw;
|
||||
|
||||
/* take care of Inf and NaN */
|
||||
if((ix0&0x7ff00000)==0x7ff00000) {
|
||||
return x*x+x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf
|
||||
sqrt(-inf)=sNaN */
|
||||
}
|
||||
/* take care of zero */
|
||||
if(ix0<=0) {
|
||||
if(((ix0&(~sign))|ix1)==0) return x;/* sqrt(+-0) = +-0 */
|
||||
else if(ix0<0)
|
||||
return (x-x)/(x-x); /* sqrt(-ve) = sNaN */
|
||||
}
|
||||
/* normalize x */
|
||||
m = (ix0>>20);
|
||||
if(m==0) { /* subnormal x */
|
||||
while(ix0==0) {
|
||||
m -= 21;
|
||||
ix0 |= (ix1>>11); ix1 <<= 21;
|
||||
}
|
||||
for(i=0;(ix0&0x00100000)==0;i++) ix0<<=1;
|
||||
m -= i-1;
|
||||
ix0 |= (ix1>>(32-i));
|
||||
ix1 <<= i;
|
||||
}
|
||||
m -= 1023; /* unbias exponent */
|
||||
ix0 = (ix0&0x000fffff)|0x00100000;
|
||||
if(m&1){ /* odd m, double x to make it even */
|
||||
ix0 += ix0 + ((ix1&sign)>>31);
|
||||
ix1 += ix1;
|
||||
}
|
||||
m >>= 1; /* m = [m/2] */
|
||||
|
||||
/* generate sqrt(x) bit by bit */
|
||||
ix0 += ix0 + ((ix1&sign)>>31);
|
||||
ix1 += ix1;
|
||||
q = q1 = s0 = s1 = 0; /* [q,q1] = sqrt(x) */
|
||||
r = 0x00200000; /* r = moving bit from right to left */
|
||||
|
||||
while(r!=0) {
|
||||
t = s0+r;
|
||||
if(t<=ix0) {
|
||||
s0 = t+r;
|
||||
ix0 -= t;
|
||||
q += r;
|
||||
}
|
||||
ix0 += ix0 + ((ix1&sign)>>31);
|
||||
ix1 += ix1;
|
||||
r>>=1;
|
||||
}
|
||||
|
||||
r = sign;
|
||||
while(r!=0) {
|
||||
t1 = s1+r;
|
||||
t = s0;
|
||||
if((t<ix0)||((t==ix0)&&(t1<=ix1))) {
|
||||
s1 = t1+r;
|
||||
if(((t1&sign)==sign)&&(s1&sign)==0) s0 += 1;
|
||||
ix0 -= t;
|
||||
if (ix1 < t1) ix0 -= 1;
|
||||
ix1 -= t1;
|
||||
q1 += r;
|
||||
}
|
||||
ix0 += ix0 + ((ix1&sign)>>31);
|
||||
ix1 += ix1;
|
||||
r>>=1;
|
||||
}
|
||||
|
||||
/* use floating add to find out rounding direction */
|
||||
if((ix0|ix1)!=0) {
|
||||
z = one-tiny; /* trigger inexact flag */
|
||||
if (z>=one) {
|
||||
z = one+tiny;
|
||||
if (q1==(uint32_t)0xffffffff) { q1=0; q += 1;}
|
||||
else if (z>one) {
|
||||
if (q1==(uint32_t)0xfffffffe) q+=1;
|
||||
q1+=2;
|
||||
} else
|
||||
q1 += (q1&1);
|
||||
}
|
||||
}
|
||||
ix0 = (q>>1)+0x3fe00000;
|
||||
ix1 = q1>>1;
|
||||
if ((q&1)==1) ix1 |= sign;
|
||||
ix0 += (m <<20);
|
||||
|
||||
u.msw = ix0;
|
||||
u.lsw = ix1;
|
||||
return u.f;
|
||||
}
|
||||
|
||||
/* called by libunwind */
|
||||
int fprintf(FILE *stream, const char *fmt, ...)
|
||||
{
|
||||
struct msg_log request;
|
||||
|
||||
request.type = MESSAGE_TYPE_LOG;
|
||||
request.fmt = fmt;
|
||||
va_start(request.args, fmt);
|
||||
mailbox_send_and_wait(&request);
|
||||
va_end(request.args);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* called by libunwind */
|
||||
int dladdr (const void *address, Dl_info *info)
|
||||
{
|
||||
/* we don't try to resolve names */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* called by libunwind */
|
||||
int dl_iterate_phdr (int (*callback)(struct dl_phdr_info *, size_t, void *), void *data)
|
||||
{
|
||||
Elf32_Ehdr *ehdr;
|
||||
struct dl_phdr_info phdr_info;
|
||||
int retval;
|
||||
|
||||
ehdr = (Elf32_Ehdr *)(KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE);
|
||||
phdr_info = (struct dl_phdr_info){
|
||||
.dlpi_addr = 0, /* absolutely linked */
|
||||
.dlpi_name = "<ksupport>",
|
||||
.dlpi_phdr = (Elf32_Phdr*) ((intptr_t)ehdr + ehdr->e_phoff),
|
||||
.dlpi_phnum = ehdr->e_phnum,
|
||||
};
|
||||
retval = callback(&phdr_info, sizeof(phdr_info), data);
|
||||
if(retval)
|
||||
return retval;
|
||||
|
||||
ehdr = (Elf32_Ehdr *)KERNELCPU_PAYLOAD_ADDRESS;
|
||||
phdr_info = (struct dl_phdr_info){
|
||||
.dlpi_addr = KERNELCPU_PAYLOAD_ADDRESS,
|
||||
.dlpi_name = "<kernel>",
|
||||
.dlpi_phdr = (Elf32_Phdr*) ((intptr_t)ehdr + ehdr->e_phoff),
|
||||
.dlpi_phnum = ehdr->e_phnum,
|
||||
};
|
||||
retval = callback(&phdr_info, sizeof(phdr_info), data);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static Elf32_Addr resolve_runtime_export(void *resolve_data,
|
||||
const char *name)
|
||||
{
|
||||
const struct symbol *sym = runtime_exports;
|
||||
while(sym->name) {
|
||||
if(!strcmp(sym->name, name))
|
||||
return (Elf32_Addr)sym->addr;
|
||||
++sym;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void exception_handler(unsigned long vect, unsigned long *regs,
|
||||
unsigned long pc, unsigned long ea);
|
||||
void exception_handler(unsigned long vect, unsigned long *regs,
|
||||
unsigned long pc, unsigned long ea)
|
||||
{
|
||||
artiq_raise_from_c("InternalError",
|
||||
"Hardware exception {0} at PC 0x{1:08x}, EA 0x{2:08x}",
|
||||
vect, pc, ea);
|
||||
}
|
||||
|
||||
static void now_init(void)
|
||||
{
|
||||
struct msg_base request;
|
||||
struct msg_now_init_reply *reply;
|
||||
|
||||
request.type = MESSAGE_TYPE_NOW_INIT_REQUEST;
|
||||
mailbox_send_and_wait(&request);
|
||||
|
||||
reply = mailbox_wait_and_receive();
|
||||
if(reply->type != MESSAGE_TYPE_NOW_INIT_REPLY) {
|
||||
core_log("Malformed MESSAGE_TYPE_NOW_INIT_REQUEST reply type %d\n",
|
||||
reply->type);
|
||||
while(1);
|
||||
}
|
||||
now = reply->now;
|
||||
mailbox_acknowledge();
|
||||
}
|
||||
|
||||
static void now_save(void)
|
||||
{
|
||||
struct msg_now_save request;
|
||||
|
||||
request.type = MESSAGE_TYPE_NOW_SAVE;
|
||||
request.now = now;
|
||||
mailbox_send_and_wait(&request);
|
||||
}
|
||||
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
struct msg_load_request *request = mailbox_receive();
|
||||
struct msg_load_reply load_reply = {
|
||||
.type = MESSAGE_TYPE_LOAD_REPLY,
|
||||
.error = NULL
|
||||
};
|
||||
|
||||
if(request == NULL) {
|
||||
bridge_main();
|
||||
while(1);
|
||||
}
|
||||
|
||||
if(request->library != NULL) {
|
||||
if(!dyld_load(request->library, KERNELCPU_PAYLOAD_ADDRESS,
|
||||
resolve_runtime_export, NULL, request->library_info,
|
||||
&load_reply.error)) {
|
||||
mailbox_send(&load_reply);
|
||||
while(1);
|
||||
}
|
||||
|
||||
void *__bss_start = dyld_lookup("__bss_start", request->library_info);
|
||||
void *_end = dyld_lookup("_end", request->library_info);
|
||||
memset(__bss_start, 0, _end - __bss_start);
|
||||
}
|
||||
|
||||
if(request->run_kernel) {
|
||||
void (*kernel_run)() = dyld_lookup("__modinit__", request->library_info);
|
||||
void *typeinfo = dyld_lookup("typeinfo", request->library_info);
|
||||
|
||||
mailbox_send_and_wait(&load_reply);
|
||||
|
||||
now_init();
|
||||
kernel_run();
|
||||
now_save();
|
||||
|
||||
attribute_writeback(typeinfo);
|
||||
|
||||
struct msg_base finished_reply;
|
||||
finished_reply.type = MESSAGE_TYPE_FINISHED;
|
||||
mailbox_send_and_wait(&finished_reply);
|
||||
} else {
|
||||
mailbox_send(&load_reply);
|
||||
}
|
||||
|
||||
while(1);
|
||||
}
|
||||
|
||||
/* called from __artiq_personality */
|
||||
void __artiq_terminate(struct artiq_exception *artiq_exn,
|
||||
struct artiq_backtrace_item *backtrace,
|
||||
size_t backtrace_size)
|
||||
{
|
||||
struct msg_exception msg;
|
||||
|
||||
now_save();
|
||||
|
||||
msg.type = MESSAGE_TYPE_EXCEPTION;
|
||||
msg.exception = artiq_exn;
|
||||
msg.backtrace = backtrace;
|
||||
msg.backtrace_size = backtrace_size;
|
||||
mailbox_send(&msg);
|
||||
|
||||
while(1);
|
||||
}
|
||||
|
||||
void ksupport_abort()
|
||||
{
|
||||
artiq_raise_from_c("InternalError", "abort() called; check device log for details",
|
||||
0, 0, 0);
|
||||
}
|
||||
|
||||
int watchdog_set(int ms)
|
||||
{
|
||||
struct msg_watchdog_set_request request;
|
||||
struct msg_watchdog_set_reply *reply;
|
||||
int id;
|
||||
|
||||
request.type = MESSAGE_TYPE_WATCHDOG_SET_REQUEST;
|
||||
request.ms = ms;
|
||||
mailbox_send_and_wait(&request);
|
||||
|
||||
reply = mailbox_wait_and_receive();
|
||||
if(reply->type != MESSAGE_TYPE_WATCHDOG_SET_REPLY) {
|
||||
core_log("Malformed MESSAGE_TYPE_WATCHDOG_SET_REQUEST reply type %d\n",
|
||||
reply->type);
|
||||
while(1);
|
||||
}
|
||||
id = reply->id;
|
||||
mailbox_acknowledge();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void watchdog_clear(int id)
|
||||
{
|
||||
struct msg_watchdog_clear request;
|
||||
|
||||
request.type = MESSAGE_TYPE_WATCHDOG_CLEAR;
|
||||
request.id = id;
|
||||
mailbox_send_and_wait(&request);
|
||||
}
|
||||
|
||||
void send_rpc(int service, const char *tag, ...)
|
||||
{
|
||||
struct msg_rpc_send request;
|
||||
|
||||
if(service != 0)
|
||||
request.type = MESSAGE_TYPE_RPC_SEND;
|
||||
else
|
||||
request.type = MESSAGE_TYPE_RPC_BATCH;
|
||||
request.service = service;
|
||||
request.tag = tag;
|
||||
va_start(request.args, tag);
|
||||
mailbox_send_and_wait(&request);
|
||||
va_end(request.args);
|
||||
}
|
||||
|
||||
int recv_rpc(void *slot)
|
||||
{
|
||||
struct msg_rpc_recv_request request;
|
||||
struct msg_rpc_recv_reply *reply;
|
||||
|
||||
request.type = MESSAGE_TYPE_RPC_RECV_REQUEST;
|
||||
request.slot = slot;
|
||||
mailbox_send_and_wait(&request);
|
||||
|
||||
reply = mailbox_wait_and_receive();
|
||||
if(reply->type != MESSAGE_TYPE_RPC_RECV_REPLY) {
|
||||
core_log("Malformed MESSAGE_TYPE_RPC_RECV_REQUEST reply type %d\n",
|
||||
reply->type);
|
||||
while(1);
|
||||
}
|
||||
|
||||
if(reply->exception) {
|
||||
struct artiq_exception exception;
|
||||
memcpy(&exception, reply->exception,
|
||||
sizeof(struct artiq_exception));
|
||||
mailbox_acknowledge();
|
||||
__artiq_raise(&exception);
|
||||
} else {
|
||||
int alloc_size = reply->alloc_size;
|
||||
mailbox_acknowledge();
|
||||
return alloc_size;
|
||||
}
|
||||
}
|
||||
|
||||
struct attr_desc {
|
||||
uint32_t offset;
|
||||
const char *tag;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct type_desc {
|
||||
struct attr_desc **attributes;
|
||||
void **objects;
|
||||
};
|
||||
|
||||
void attribute_writeback(void *utypes)
|
||||
{
|
||||
struct type_desc **types = (struct type_desc **)utypes;
|
||||
while(*types) {
|
||||
struct type_desc *type = *types++;
|
||||
|
||||
size_t attr_count = 0;
|
||||
for(struct attr_desc **attr = type->attributes; *attr; attr++)
|
||||
attr_count++;
|
||||
|
||||
void **objects = type->objects;
|
||||
while(*objects) {
|
||||
void *object = *objects++;
|
||||
|
||||
struct attr_desc **attrs = type->attributes;
|
||||
while(*attrs) {
|
||||
struct attr_desc *attr = *attrs++;
|
||||
|
||||
if(attr->tag) {
|
||||
uintptr_t value = (uintptr_t)object + attr->offset;
|
||||
send_rpc(0, attr->tag, &object, &attr->name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct artiq_list cache_get(const char *key)
|
||||
{
|
||||
struct msg_cache_get_request request;
|
||||
struct msg_cache_get_reply *reply;
|
||||
|
||||
request.type = MESSAGE_TYPE_CACHE_GET_REQUEST;
|
||||
request.key = key;
|
||||
mailbox_send_and_wait(&request);
|
||||
|
||||
reply = mailbox_wait_and_receive();
|
||||
if(reply->type != MESSAGE_TYPE_CACHE_GET_REPLY) {
|
||||
core_log("Malformed MESSAGE_TYPE_CACHE_GET_REQUEST reply type %d\n",
|
||||
reply->type);
|
||||
while(1);
|
||||
}
|
||||
|
||||
return (struct artiq_list) { reply->length, reply->elements };
|
||||
}
|
||||
|
||||
void cache_put(const char *key, struct artiq_list value)
|
||||
{
|
||||
struct msg_cache_put_request request;
|
||||
struct msg_cache_put_reply *reply;
|
||||
|
||||
request.type = MESSAGE_TYPE_CACHE_PUT_REQUEST;
|
||||
request.key = key;
|
||||
request.elements = value.elements;
|
||||
request.length = value.length;
|
||||
mailbox_send_and_wait(&request);
|
||||
|
||||
reply = mailbox_wait_and_receive();
|
||||
if(reply->type != MESSAGE_TYPE_CACHE_PUT_REPLY) {
|
||||
core_log("Malformed MESSAGE_TYPE_CACHE_PUT_REQUEST reply type %d\n",
|
||||
reply->type);
|
||||
while(1);
|
||||
}
|
||||
|
||||
if(!reply->succeeded) {
|
||||
artiq_raise_from_c("CacheError",
|
||||
"cannot put into a busy cache row",
|
||||
0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void core_log(const char *fmt, ...)
|
||||
{
|
||||
struct msg_log request;
|
||||
|
||||
request.type = MESSAGE_TYPE_LOG;
|
||||
request.fmt = fmt;
|
||||
va_start(request.args, fmt);
|
||||
mailbox_send_and_wait(&request);
|
||||
va_end(request.args);
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
#ifndef __KSTARTUP_H
|
||||
#define __KSTARTUP_H
|
||||
|
||||
struct artiq_list {
|
||||
int32_t length;
|
||||
int32_t *elements;
|
||||
};
|
||||
|
||||
int watchdog_set(int ms);
|
||||
void watchdog_clear(int id);
|
||||
void send_rpc(int service, const char *tag, ...);
|
||||
int recv_rpc(void *slot);
|
||||
struct artiq_list cache_get(const char *key);
|
||||
void cache_put(const char *key, struct artiq_list value);
|
||||
void core_log(const char *fmt, ...);
|
||||
|
||||
#endif /* __KSTARTUP_H */
|
@ -1,20 +1,23 @@
|
||||
INCLUDE generated/output_format.ld
|
||||
STARTUP(crt0-or1k.o)
|
||||
ENTRY(_start)
|
||||
|
||||
INCLUDE generated/regions.ld
|
||||
|
||||
/* First 32M of main memory are reserved for runtime
|
||||
/* First 4M of main memory are reserved for runtime
|
||||
* code/data/heap, then comes kernel memory.
|
||||
* First 128K of kernel memory are for support code.
|
||||
* Next 4M of main memory are reserved for
|
||||
* the background RPC queue.
|
||||
* First 256K of kernel memory are for support code.
|
||||
* Support code is loaded at ORIGIN-0x80 so that ELF headers
|
||||
* are also loaded.
|
||||
*/
|
||||
MEMORY {
|
||||
ksupport (RWX) : ORIGIN = 0x42000000, LENGTH = 0x20000
|
||||
ksupport (RWX) : ORIGIN = 0x40800000, LENGTH = 0x40000
|
||||
}
|
||||
|
||||
/* On AMP systems, kernel stack is at the end of main RAM,
|
||||
* before the runtime stack. Leave 1M for runtime stack.
|
||||
*/
|
||||
PROVIDE(_fstack = 0x40000000 + LENGTH(main_ram) - 1024*1024 - 4);
|
||||
/* Kernel stack is at the end of main RAM. */
|
||||
PROVIDE(_fstack = ORIGIN(main_ram) + LENGTH(main_ram) - 4);
|
||||
|
||||
/* Force ld to make the ELF header as loadable. */
|
||||
PHDRS
|
||||
@ -32,6 +35,16 @@ SECTIONS
|
||||
_etext = .;
|
||||
} :text
|
||||
|
||||
/* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */
|
||||
.got : {
|
||||
_GLOBAL_OFFSET_TABLE_ = .;
|
||||
*(.got)
|
||||
} :text
|
||||
|
||||
.got.plt : {
|
||||
*(.got.plt)
|
||||
} :text
|
||||
|
||||
.rodata :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
@ -43,12 +56,12 @@ SECTIONS
|
||||
|
||||
.eh_frame :
|
||||
{
|
||||
*(.eh_frame)
|
||||
KEEP(*(.eh_frame))
|
||||
} :text
|
||||
|
||||
.eh_frame_hdr :
|
||||
{
|
||||
*(.eh_frame_hdr)
|
||||
KEEP(*(.eh_frame_hdr))
|
||||
} :text :eh_frame
|
||||
|
||||
.data :
|
||||
@ -73,7 +86,10 @@ SECTIONS
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_ebss = .;
|
||||
. = ALIGN(8);
|
||||
_heapstart = .;
|
||||
}
|
||||
|
||||
/DISCARD/ :
|
||||
{
|
||||
*(.debug*)
|
||||
}
|
||||
}
|
||||
|
121
artiq/runtime/ksupport_glue.c
Normal file
121
artiq/runtime/ksupport_glue.c
Normal file
@ -0,0 +1,121 @@
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <link.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
void send_to_log(const char *ptr, size_t length);
|
||||
|
||||
#define KERNELCPU_EXEC_ADDRESS 0x40800000
|
||||
#define KERNELCPU_PAYLOAD_ADDRESS 0x40840000
|
||||
#define KERNELCPU_LAST_ADDRESS 0x4fffffff
|
||||
#define KSUPPORT_HEADER_SIZE 0x80
|
||||
|
||||
/* called by libunwind */
|
||||
int fprintf(FILE *stream, const char *fmt, ...)
|
||||
{
|
||||
size_t size;
|
||||
char *buf;
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
size = vsnprintf(NULL, 0, fmt, args);
|
||||
buf = __builtin_alloca(size + 1);
|
||||
va_end(args);
|
||||
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buf, size + 1, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
send_to_log(buf, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* called by libunwind */
|
||||
int dladdr (const void *address, Dl_info *info)
|
||||
{
|
||||
/* we don't try to resolve names */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* called by libunwind */
|
||||
int dl_iterate_phdr (int (*callback)(struct dl_phdr_info *, size_t, void *), void *data)
|
||||
{
|
||||
Elf32_Ehdr *ehdr;
|
||||
struct dl_phdr_info phdr_info;
|
||||
int retval;
|
||||
|
||||
ehdr = (Elf32_Ehdr *)(KERNELCPU_EXEC_ADDRESS - KSUPPORT_HEADER_SIZE);
|
||||
phdr_info = (struct dl_phdr_info){
|
||||
.dlpi_addr = 0, /* absolutely linked */
|
||||
.dlpi_name = "<ksupport>",
|
||||
.dlpi_phdr = (Elf32_Phdr*) ((intptr_t)ehdr + ehdr->e_phoff),
|
||||
.dlpi_phnum = ehdr->e_phnum,
|
||||
};
|
||||
retval = callback(&phdr_info, sizeof(phdr_info), data);
|
||||
if(retval)
|
||||
return retval;
|
||||
|
||||
ehdr = (Elf32_Ehdr *)KERNELCPU_PAYLOAD_ADDRESS;
|
||||
phdr_info = (struct dl_phdr_info){
|
||||
.dlpi_addr = KERNELCPU_PAYLOAD_ADDRESS,
|
||||
.dlpi_name = "<kernel>",
|
||||
.dlpi_phdr = (Elf32_Phdr*) ((intptr_t)ehdr + ehdr->e_phoff),
|
||||
.dlpi_phnum = ehdr->e_phnum,
|
||||
};
|
||||
retval = callback(&phdr_info, sizeof(phdr_info), data);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* called by kernel */
|
||||
double round(double x);
|
||||
double round(double x)
|
||||
{
|
||||
union {double f; uint64_t i;} u = {x};
|
||||
int e = u.i >> 52 & 0x7ff;
|
||||
double y;
|
||||
|
||||
if (e >= 0x3ff+52)
|
||||
return x;
|
||||
if (u.i >> 63)
|
||||
x = -x;
|
||||
if (e < 0x3ff-1) {
|
||||
/* we don't do it in ARTIQ */
|
||||
/* raise inexact if x!=0 */
|
||||
// FORCE_EVAL(x + 0x1p52);
|
||||
return 0*u.f;
|
||||
}
|
||||
y = (double)(x + 0x1p52) - 0x1p52 - x;
|
||||
if (y > 0.5)
|
||||
y = y + x - 1;
|
||||
else if (y <= -0.5)
|
||||
y = y + x + 1;
|
||||
else
|
||||
y = y + x;
|
||||
if (u.i >> 63)
|
||||
y = -y;
|
||||
return y;
|
||||
}
|
||||
|
||||
/* called by kernel */
|
||||
int core_log(const char *fmt, ...);
|
||||
int core_log(const char *fmt, ...)
|
||||
{
|
||||
size_t size;
|
||||
char *buf;
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
size = vsnprintf(NULL, 0, fmt, args);
|
||||
buf = __builtin_alloca(size + 1);
|
||||
va_end(args);
|
||||
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buf, size + 1, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
send_to_log(buf, size);
|
||||
return 0;
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <console.h>
|
||||
|
||||
#include <generated/csr.h>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
static int buffer_cursor;
|
||||
static char buffer[LOG_BUFFER_SIZE];
|
||||
|
||||
void core_log(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
core_log_va(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void core_log_va(const char *fmt, va_list args)
|
||||
{
|
||||
char outbuf[256];
|
||||
int len = vscnprintf(outbuf, sizeof(outbuf), fmt, args);
|
||||
|
||||
for(int i = 0; i < len; i++) {
|
||||
buffer[buffer_cursor] = outbuf[i];
|
||||
buffer_cursor = (buffer_cursor + 1) % LOG_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
#ifdef CSR_ETHMAC_BASE
|
||||
/* Since main comms are over ethernet, the serial port
|
||||
* is free for us to use. */
|
||||
putsnonl(outbuf);
|
||||
#endif
|
||||
}
|
||||
|
||||
void core_log_get(char *outbuf)
|
||||
{
|
||||
int j = buffer_cursor;
|
||||
for(int i = 0; i < LOG_BUFFER_SIZE; i++) {
|
||||
outbuf[i] = buffer[j];
|
||||
j = (j + 1) % LOG_BUFFER_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
void core_log_clear()
|
||||
{
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
#ifndef __LOG_H
|
||||
#define __LOG_H
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#define LOG_BUFFER_SIZE 4096
|
||||
|
||||
void core_log(const char *fmt, ...);
|
||||
void core_log_va(const char *fmt, va_list args);
|
||||
void core_log_get(char *outbuf);
|
||||
void core_log_clear(void);
|
||||
|
||||
#endif /* __LOG_H */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user