forked from M-Labs/artiq
1
0
Fork 0

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:
Robert Jördens 2016-11-13 17:30:37 +01:00
commit b9ce2bb1f0
135 changed files with 4830 additions and 5126 deletions

View File

@ -1,4 +1,5 @@
graft artiq/runtime graft artiq/runtime
graft artiq/runtime.rs
graft artiq/examples graft artiq/examples
include artiq/gui/logo*.svg include artiq/gui/logo*.svg
include versioneer.py include versioneer.py

View File

@ -13,6 +13,9 @@ Release notes
* Dynamic __getattr__'s returning RPC target methods are not supported anymore. * Dynamic __getattr__'s returning RPC target methods are not supported anymore.
Controller driver classes must define all their methods intended for RPC as Controller driver classes must define all their methods intended for RPC as
members. 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 2.0

View File

@ -187,8 +187,16 @@ class FilesDock(QtWidgets.QDockWidget):
except: except:
logger.warning("unable to read metadata from %s", logger.warning("unable to read metadata from %s",
info.filePath(), exc_info=True) info.filePath(), exc_info=True)
rd = dict()
if "archive" in f:
rd = {k: (True, v.value) for k, v in f["archive"].items()}
if "datasets" in f: 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.datasets.init(rd)
self.dataset_changed.emit(info.filePath()) self.dataset_changed.emit(info.filePath())

View File

@ -994,8 +994,24 @@ class Stitcher:
else: else:
assert False 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, 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 self.functions[function] = function_type
return function_type return function_type
@ -1007,7 +1023,11 @@ class Stitcher:
if function in self.functions: if function in self.functions:
pass 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) self._quote_rpc(function, loc)
elif host_function.artiq_embedded.function is not None: elif host_function.artiq_embedded.function is not None:
if host_function.__name__ == "<lambda>": if host_function.__name__ == "<lambda>":

View File

@ -40,7 +40,8 @@ class Source:
return cls(source.Buffer(f.read(), filename, 1), engine=engine) return cls(source.Buffer(f.read(), filename, 1), engine=engine)
class Module: 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.engine = src.engine
self.embedding_map = src.embedding_map self.embedding_map = src.embedding_map
self.name = src.name self.name = src.name
@ -79,14 +80,8 @@ class Module:
llvm_ir_generator = transforms.LLVMIRGenerator( llvm_ir_generator = transforms.LLVMIRGenerator(
engine=self.engine, module_name=self.name, target=target, engine=self.engine, module_name=self.name, target=target,
embedding_map=self.embedding_map) embedding_map=self.embedding_map)
return llvm_ir_generator.process(self.artiq_ir, attribute_writeback=True) return llvm_ir_generator.process(self.artiq_ir,
attribute_writeback=self.attribute_writeback)
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__"
def __repr__(self): def __repr__(self):
printer = types.TypePrinter() printer = types.TypePrinter()

View File

@ -31,6 +31,7 @@ def globals():
# ARTIQ decorators # ARTIQ decorators
"kernel": builtins.fn_kernel(), "kernel": builtins.fn_kernel(),
"portable": builtins.fn_kernel(), "portable": builtins.fn_kernel(),
"rpc": builtins.fn_kernel(),
# ARTIQ context managers # ARTIQ context managers
"parallel": builtins.obj_parallel(), "parallel": builtins.obj_parallel(),

View File

@ -86,14 +86,11 @@ class Target:
llmachine = lltarget.create_target_machine( llmachine = lltarget.create_target_machine(
features=",".join(["+{}".format(f) for f in self.features]), features=",".join(["+{}".format(f) for f in self.features]),
reloc="pic", codemodel="default") reloc="pic", codemodel="default")
llmachine.set_verbose(True) llmachine.set_asm_verbosity(True)
return llmachine return llmachine
def optimize(self, llmodule): def optimize(self, llmodule):
llmachine = self.target_machine()
llpassmgr = llvm.create_module_pass_manager() llpassmgr = llvm.create_module_pass_manager()
llmachine.target_data.add_pass(llpassmgr)
llmachine.add_analysis_passes(llpassmgr)
# Register our alias analysis passes. # Register our alias analysis passes.
llpassmgr.add_basic_alias_analysis_pass() llpassmgr.add_basic_alias_analysis_pass()
@ -161,9 +158,9 @@ class Target:
return llmachine.emit_object(llmodule) 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.""" """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))] + ["{{obj{}}}".format(index) for index in range(len(objects))] +
["-o", "{output}"], ["-o", "{output}"],
output=b"", output=b"",
@ -177,8 +174,7 @@ class Target:
return library return library
def compile_and_link(self, modules): def compile_and_link(self, modules):
return self.link([self.assemble(self.compile(module)) for module in modules], return self.link([self.assemble(self.compile(module)) for module in modules])
init_fn=modules[0].entry_point())
def strip(self, library): def strip(self, library):
with RunTool([self.triple + "-strip", "--strip-debug", "{library}", "-o", "{output}"], with RunTool([self.triple + "-strip", "--strip-debug", "{library}", "-o", "{output}"],

View File

@ -47,7 +47,7 @@ def main():
target = OR1KTarget() target = OR1KTarget()
llvm_ir = target.compile(module) llvm_ir = target.compile(module)
elf_obj = target.assemble(llvm_ir) 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(), benchmark(lambda: embed(),
"ARTIQ embedding") "ARTIQ embedding")
@ -61,7 +61,7 @@ def main():
benchmark(lambda: target.assemble(llvm_ir), benchmark(lambda: target.assemble(llvm_ir),
"LLVM machine code emission") "LLVM machine code emission")
benchmark(lambda: target.link([elf_obj], init_fn=module.entry_point()), benchmark(lambda: target.link([elf_obj]),
"Linking") "Linking")
benchmark(lambda: target.strip(elf_shlib), benchmark(lambda: target.strip(elf_shlib),

View File

@ -452,6 +452,9 @@ class ARTIQIRGenerator(algorithm.Visitor):
self.current_block = body self.current_block = body
self.visit(node.body) self.visit(node.body)
post_body = self.current_block post_body = self.current_block
finally:
self.break_target = old_break
self.continue_target = old_continue
if any(node.orelse): if any(node.orelse):
else_tail = self.add_block("while.else") else_tail = self.add_block("while.else")
@ -472,9 +475,6 @@ class ARTIQIRGenerator(algorithm.Visitor):
if not post_body.is_terminated(): if not post_body.is_terminated():
post_body.append(ir.Branch(head)) post_body.append(ir.Branch(head))
break_block.append(ir.Branch(tail)) break_block.append(ir.Branch(tail))
finally:
self.break_target = old_break
self.continue_target = old_continue
def iterable_len(self, value, typ=_size_type): def iterable_len(self, value, typ=_size_type):
if builtins.is_listish(value.type): if builtins.is_listish(value.type):
@ -541,6 +541,9 @@ class ARTIQIRGenerator(algorithm.Visitor):
self.current_assign = None self.current_assign = None
self.visit(node.body) self.visit(node.body)
post_body = self.current_block post_body = self.current_block
finally:
self.break_target = old_break
self.continue_target = old_continue
if any(node.orelse): if any(node.orelse):
else_tail = self.add_block("for.else") else_tail = self.add_block("for.else")
@ -564,9 +567,6 @@ class ARTIQIRGenerator(algorithm.Visitor):
if not post_body.is_terminated(): if not post_body.is_terminated():
post_body.append(ir.Branch(continue_block)) post_body.append(ir.Branch(continue_block))
break_block.append(ir.Branch(tail)) break_block.append(ir.Branch(tail))
finally:
self.break_target = old_break
self.continue_target = old_continue
def visit_Break(self, node): def visit_Break(self, node):
self.append(ir.Branch(self.break_target)) self.append(ir.Branch(self.break_target))
@ -821,7 +821,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
timeout = self.visit(context_expr_node.args[0]) timeout = self.visit(context_expr_node.args[0])
timeout_ms = self.append(ir.Arith(ast.Mult(loc=None), timeout, timeout_ms = self.append(ir.Arith(ast.Mult(loc=None), timeout,
ir.Constant(1000, builtins.TFloat()))) 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], watchdog_id = self.append(ir.Builtin("watchdog_set", [timeout_ms_int],
builtins.TInt32())) builtins.TInt32()))

View File

@ -509,7 +509,7 @@ class ASTTypedRewriter(algorithm.Transformer):
visit_DictComp = visit_unsupported visit_DictComp = visit_unsupported
visit_Ellipsis = visit_unsupported visit_Ellipsis = visit_unsupported
visit_GeneratorExp = visit_unsupported visit_GeneratorExp = visit_unsupported
visit_Set = visit_unsupported # visit_Set = visit_unsupported
visit_SetComp = visit_unsupported visit_SetComp = visit_unsupported
visit_Starred = visit_unsupported visit_Starred = visit_unsupported
visit_Yield = visit_unsupported visit_Yield = visit_unsupported

View File

@ -19,6 +19,7 @@ lli32 = ll.IntType(32)
lli64 = ll.IntType(64) lli64 = ll.IntType(64)
lldouble = ll.DoubleType() lldouble = ll.DoubleType()
llptr = ll.IntType(8).as_pointer() llptr = ll.IntType(8).as_pointer()
llptrptr = ll.IntType(8).as_pointer().as_pointer()
llmetadata = ll.MetaData() llmetadata = ll.MetaData()
@ -36,9 +37,16 @@ def memoize(generator):
class DebugInfoEmitter: class DebugInfoEmitter:
def __init__(self, llmodule): def __init__(self, llmodule):
self.llmodule = llmodule self.llmodule = llmodule
self.llsubprograms = [] self.llcompileunit = None
self.cache = {} 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 emit_metadata(self, operands):
def map_operand(operand): def map_operand(operand):
if operand is None: if operand is None:
@ -66,14 +74,13 @@ class DebugInfoEmitter:
}) })
@memoize @memoize
def emit_compile_unit(self, source_buffer, llsubprograms): def emit_compile_unit(self, source_buffer):
return self.emit_debug_info("DICompileUnit", { return self.emit_debug_info("DICompileUnit", {
"language": ll.DIToken("DW_LANG_Python"), "language": ll.DIToken("DW_LANG_Python"),
"file": self.emit_file(source_buffer), "file": self.emit_file(source_buffer),
"producer": "ARTIQ", "producer": "ARTIQ",
"runtimeVersion": 0, "runtimeVersion": 0,
"emissionKind": 2, # full=1, lines only=2 "emissionKind": 2, # full=1, lines only=2
"subprograms": self.emit_metadata(llsubprograms)
}, is_distinct=True) }, is_distinct=True)
@memoize @memoize
@ -85,21 +92,26 @@ class DebugInfoEmitter:
@memoize @memoize
def emit_subprogram(self, func, llfunc): def emit_subprogram(self, func, llfunc):
source_buffer = func.loc.source_buffer 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)) 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, "name": func.name,
"linkageName": llfunc.name, "linkageName": llfunc.name,
"type": self.emit_subroutine_type(func.type), "type": self.emit_subroutine_type(func.type),
"file": self.emit_file(source_buffer), "file": self.emit_file(source_buffer),
"line": func.loc.line(), "line": func.loc.line(),
"unit": self.llcompileunit,
"scope": self.emit_file(source_buffer), "scope": self.emit_file(source_buffer),
"scopeLine": func.loc.line(), "scopeLine": func.loc.line(),
"isLocal": func.is_internal, "isLocal": func.is_internal,
"isDefinition": True, "isDefinition": True,
"variables": self.emit_metadata([]) "variables": self.emit_metadata([])
}, is_distinct=True) }, is_distinct=True)
self.llsubprograms.append(llsubprogram)
return llsubprogram
@memoize @memoize
def emit_loc(self, loc, scope): def emit_loc(self, loc, scope):
@ -109,18 +121,6 @@ class DebugInfoEmitter:
"scope": scope "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: class LLVMIRGenerator:
def __init__(self, engine, module_name, target, embedding_map): def __init__(self, engine, module_name, target, embedding_map):
self.engine = engine self.engine = engine
@ -349,14 +349,15 @@ class LLVMIRGenerator:
elif name == "strcmp": elif name == "strcmp":
llty = ll.FunctionType(lli32, [llptr, llptr]) llty = ll.FunctionType(lli32, [llptr, llptr])
elif name == "send_rpc": elif name == "send_rpc":
llty = ll.FunctionType(llvoid, [lli32, llptr], llty = ll.FunctionType(llvoid, [lli32, llptr, llptrptr])
var_arg=True) elif name == "send_async_rpc":
llty = ll.FunctionType(llvoid, [lli32, llptr, llptrptr])
elif name == "recv_rpc": elif name == "recv_rpc":
llty = ll.FunctionType(lli32, [llptr]) llty = ll.FunctionType(lli32, [llptr])
elif name == "now": elif name == "now":
llty = lli64 llty = lli64
elif name == "watchdog_set": elif name == "watchdog_set":
llty = ll.FunctionType(lli32, [lli32]) llty = ll.FunctionType(lli32, [lli64])
elif name == "watchdog_clear": elif name == "watchdog_clear":
llty = ll.FunctionType(llvoid, [lli32]) llty = ll.FunctionType(llvoid, [lli32])
else: else:
@ -366,7 +367,8 @@ class LLVMIRGenerator:
llglobal = ll.Function(self.llmodule, llty, name) llglobal = ll.Function(self.llmodule, llty, name)
if name in ("__artiq_raise", "__artiq_reraise", "llvm.trap"): if name in ("__artiq_raise", "__artiq_reraise", "llvm.trap"):
llglobal.attributes.add("noreturn") 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): self.target.print_function):
llglobal.attributes.add("nounwind") llglobal.attributes.add("nounwind")
else: else:
@ -407,9 +409,6 @@ class LLVMIRGenerator:
for func in functions: for func in functions:
self.process_function(func) 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: if attribute_writeback and self.embedding_map is not None:
self.emit_attribute_writeback() self.emit_attribute_writeback()
@ -652,7 +651,7 @@ class LLVMIRGenerator:
llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)], llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)],
inbounds=True) inbounds=True)
llouterenv = self.llbuilder.load(llptr) 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) llouterenv.set_metadata('nonnull', self.empty_metadata)
return self.llptr_to_var(llouterenv, env_ty.params["$outer"], var_name) 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)) inbounds=True, name="ptr.{}".format(insn.name))
llvalue = self.llbuilder.load(llptr, name="val.{}".format(insn.name)) llvalue = self.llbuilder.load(llptr, name="val.{}".format(insn.name))
if types.is_instance(typ) and attr in typ.constant_attributes: 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): if isinstance(llvalue.type, ll.PointerType):
self.mark_dereferenceable(llvalue) self.mark_dereferenceable(llvalue)
return llvalue return llvalue
@ -1051,7 +1050,7 @@ class LLVMIRGenerator:
llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)], llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)],
inbounds=True) inbounds=True)
llouterenv = self.llbuilder.load(llptr) 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) llouterenv.set_metadata('nonnull', self.empty_metadata)
return self.llptr_to_var(llouterenv, env_ty.params["$outer"], var_name) return self.llptr_to_var(llouterenv, env_ty.params["$outer"], var_name)
else: else:
@ -1233,7 +1232,8 @@ class LLVMIRGenerator:
llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [], llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [],
name="rpc.stack") name="rpc.stack")
llargs = [] llargs = self.llbuilder.alloca(llptr, ll.Constant(lli32, len(args)),
name="rpc.args")
for index, arg in enumerate(args): for index, arg in enumerate(args):
if builtins.is_none(arg.type): if builtins.is_none(arg.type):
llargslot = self.llbuilder.alloca(ll.LiteralStructType([]), llargslot = self.llbuilder.alloca(ll.LiteralStructType([]),
@ -1243,19 +1243,31 @@ class LLVMIRGenerator:
llargslot = self.llbuilder.alloca(llarg.type, llargslot = self.llbuilder.alloca(llarg.type,
name="rpc.arg{}".format(index)) name="rpc.arg{}".format(index))
self.llbuilder.store(llarg, llargslot) self.llbuilder.store(llarg, llargslot)
llargs.append(llargslot) llargslot = self.llbuilder.bitcast(llargslot, llptr)
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"), self.llbuilder.call(self.llbuiltin("send_rpc"),
[llservice, lltag] + llargs) [llservice, lltag, llargs])
# Don't waste stack space on saved arguments. # Don't waste stack space on saved arguments.
self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr]) self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr])
if fun_type.async:
return ll.Undefined
# T result = { # T result = {
# void *ptr = NULL; # void *ret_ptr = alloca(sizeof(T));
# loop: int size = rpc_recv("tag", ptr); # 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; } # if(size) { ptr = alloca(size); goto loop; }
# else *(T*)ptr # else *(T*)ret_ptr
# } # }
llprehead = self.llbuilder.basic_block llprehead = self.llbuilder.basic_block
llhead = self.llbuilder.append_basic_block(name="rpc.head") llhead = self.llbuilder.append_basic_block(name="rpc.head")
@ -1270,7 +1282,7 @@ class LLVMIRGenerator:
self.llbuilder.branch(llhead) self.llbuilder.branch(llhead)
self.llbuilder.position_at_end(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) llphi.add_incoming(llslotgen, llprehead)
if llunwindblock: if llunwindblock:
llsize = self.llbuilder.invoke(self.llbuiltin("recv_rpc"), [llphi], llsize = self.llbuilder.invoke(self.llbuiltin("recv_rpc"), [llphi],
@ -1286,6 +1298,7 @@ class LLVMIRGenerator:
self.llbuilder.position_at_end(llalloc) self.llbuilder.position_at_end(llalloc)
llalloca = self.llbuilder.alloca(lli8, llsize, name="rpc.alloc") llalloca = self.llbuilder.alloca(lli8, llsize, name="rpc.alloc")
llalloca.align = 4 # maximum alignment required by OR1K ABI
llphi.add_incoming(llalloca, llalloc) llphi.add_incoming(llalloca, llalloc)
self.llbuilder.branch(llhead) self.llbuilder.branch(llhead)
@ -1371,6 +1384,7 @@ class LLVMIRGenerator:
def _quote_attributes(): def _quote_attributes():
llglobal = None llglobal = None
llfields = [] llfields = []
emit_as_constant = True
for attr in typ.attributes: for attr in typ.attributes:
if attr == "__objectid__": if attr == "__objectid__":
objectid = self.embedding_map.store_object(value) objectid = self.embedding_map.store_object(value)
@ -1391,6 +1405,8 @@ class LLVMIRGenerator:
not types.is_c_function(typ.attributes[attr])) not types.is_c_function(typ.attributes[attr]))
if is_class_function: if is_class_function:
attrvalue = self.embedding_map.specialize_function(typ.instance, attrvalue) 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], llattrvalue = self._quote(attrvalue, typ.attributes[attr],
lambda: path() + [attr]) lambda: path() + [attr])
llfields.append(llattrvalue) llfields.append(llattrvalue)
@ -1398,6 +1414,7 @@ class LLVMIRGenerator:
llclosureptr = self.get_global_closure_ptr(typ, attr) llclosureptr = self.get_global_closure_ptr(typ, attr)
llclosureptr.initializer = llattrvalue llclosureptr.initializer = llattrvalue
llglobal.global_constant = emit_as_constant
llglobal.initializer = ll.Constant(llty.pointee, llfields) llglobal.initializer = ll.Constant(llty.pointee, llfields)
llglobal.linkage = "private" llglobal.linkage = "private"
return llglobal return llglobal

View File

@ -308,20 +308,22 @@ class TRPC(Type):
:ivar ret: (:class:`Type`) :ivar ret: (:class:`Type`)
return type return type
:ivar service: (int) RPC service number :ivar service: (int) RPC service number
:ivar async: (bool) whether the RPC blocks until return
""" """
attributes = OrderedDict() attributes = OrderedDict()
def __init__(self, ret, service): def __init__(self, ret, service, async=False):
assert isinstance(ret, Type) assert isinstance(ret, Type)
self.ret, self.service = ret, service self.ret, self.service, self.async = ret, service, async
def find(self): def find(self):
return self return self
def unify(self, other): def unify(self, other):
if isinstance(other, TRPC) and \ if isinstance(other, TRPC) and \
self.service == other.service: self.service == other.service and \
self.async == other.async:
self.ret.unify(other.ret) self.ret.unify(other.ret)
elif isinstance(other, TVar): elif isinstance(other, TVar):
other.unify(self) other.unify(self)
@ -337,7 +339,8 @@ class TRPC(Type):
def __eq__(self, other): def __eq__(self, other):
return isinstance(other, TRPC) and \ return isinstance(other, TRPC) and \
self.service == other.service self.service == other.service and \
self.async == other.async
def __ne__(self, other): def __ne__(self, other):
return not (self == other) return not (self == other)
@ -727,7 +730,9 @@ class TypePrinter(object):
elif isinstance(typ, TFunction): elif isinstance(typ, TFunction):
return signature return signature
elif isinstance(typ, TRPC): 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): elif isinstance(typ, TBuiltinFunction):
return "<function {}>".format(typ.name) return "<function {}>".format(typ.name)
elif isinstance(typ, (TConstructor, TExceptionConstructor)): elif isinstance(typ, (TConstructor, TExceptionConstructor)):

View File

@ -20,7 +20,7 @@ class _H2DMsgType(Enum):
IDENT_REQUEST = 3 IDENT_REQUEST = 3
SWITCH_CLOCK = 4 SWITCH_CLOCK = 4
LOAD_LIBRARY = 5 LOAD_KERNEL = 5
RUN_KERNEL = 6 RUN_KERNEL = 6
RPC_REPLY = 7 RPC_REPLY = 7
@ -68,9 +68,7 @@ RPCKeyword = namedtuple('RPCKeyword', ['name', 'value'])
class CommGeneric: class CommGeneric:
def __init__(self): def __init__(self):
self._read_type = self._write_type = None self._read_type = None
self._read_length = 0
self._write_buffer = []
def open(self): def open(self):
"""Opens the communication channel. """Opens the communication channel.
@ -99,10 +97,6 @@ class CommGeneric:
def _read_header(self): def _read_header(self):
self.open() 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. # Wait for a synchronization sequence, 5a 5a 5a 5a.
sync_count = 0 sync_count = 0
while sync_count < 4: while sync_count < 4:
@ -113,20 +107,11 @@ class CommGeneric:
sync_count = 0 sync_count = 0
# Read message header. # 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)) (raw_type, ) = struct.unpack("B", self.read(1))
self._read_type = _D2HMsgType(raw_type) self._read_type = _D2HMsgType(raw_type)
if self._read_length < 9: logger.debug("receiving message: type=%r",
raise IOError("Read overrun in message header ({} remaining)". self._read_type)
format(self._read_length))
self._read_length -= 9
logger.debug("receiving message: type=%r length=%d",
self._read_type, self._read_length)
def _read_expect(self, ty): def _read_expect(self, ty):
if self._read_type != ty: if self._read_type != ty:
@ -138,12 +123,6 @@ class CommGeneric:
self._read_expect(ty) self._read_expect(ty)
def _read_chunk(self, length): 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) return self.read(length)
def _read_int8(self): def _read_int8(self):
@ -162,11 +141,14 @@ class CommGeneric:
(value, ) = struct.unpack(">d", self._read_chunk(8)) (value, ) = struct.unpack(">d", self._read_chunk(8))
return value return value
def _read_bool(self):
return True if self._read_int8() else False
def _read_bytes(self): def _read_bytes(self):
return self._read_chunk(self._read_int32()) return self._read_chunk(self._read_int32())
def _read_string(self): def _read_string(self):
return self._read_bytes()[:-1].decode("utf-8") return self._read_bytes().decode("utf-8")
# #
# Writer interface # Writer interface
@ -175,46 +157,38 @@ class CommGeneric:
def _write_header(self, ty): def _write_header(self, ty):
self.open() self.open()
logger.debug("preparing to send message: type=%r", ty) logger.debug("sending message: type=%r", ty)
self._write_type = ty
self._write_buffer = []
def _write_flush(self): # Write synchronization sequence and header.
# Calculate message size. self.write(struct.pack(">lB", 0x5a5a5a5a, ty.value))
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)
def _write_empty(self, ty): def _write_empty(self, ty):
self._write_header(ty) self._write_header(ty)
self._write_flush()
def _write_chunk(self, chunk): def _write_chunk(self, chunk):
self._write_buffer.append(chunk) self.write(chunk)
def _write_int8(self, value): def _write_int8(self, value):
self._write_buffer.append(struct.pack("B", value)) self.write(struct.pack("B", value))
def _write_int32(self, 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): def _write_int64(self, value):
self._write_buffer.append(struct.pack(">q", value)) self.write(struct.pack(">q", value))
def _write_float64(self, 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): def _write_bytes(self, value):
self._write_int32(len(value)) self._write_int32(len(value))
self._write_buffer.append(value) self.write(value)
def _write_string(self, value): def _write_string(self, value):
self._write_bytes(value.encode("utf-8") + b"\0") self._write_bytes(value.encode("utf-8"))
# #
# Exported APIs # Exported APIs
@ -232,7 +206,7 @@ class CommGeneric:
if runtime_id != b"AROR": if runtime_id != b"AROR":
raise UnsupportedDevice("Unsupported runtime ID: {}" raise UnsupportedDevice("Unsupported runtime ID: {}"
.format(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 \ if gateware_version != software_version and \
gateware_version + ".dirty" != software_version: gateware_version + ".dirty" != software_version:
logger.warning("Mismatch between gateware (%s) " logger.warning("Mismatch between gateware (%s) "
@ -242,7 +216,6 @@ class CommGeneric:
def switch_clock(self, external): def switch_clock(self, external):
self._write_header(_H2DMsgType.SWITCH_CLOCK) self._write_header(_H2DMsgType.SWITCH_CLOCK)
self._write_int8(external) self._write_int8(external)
self._write_flush()
self._read_empty(_D2HMsgType.CLOCK_SWITCH_COMPLETED) self._read_empty(_D2HMsgType.CLOCK_SWITCH_COMPLETED)
@ -251,7 +224,7 @@ class CommGeneric:
self._read_header() self._read_header()
self._read_expect(_D2HMsgType.LOG_REPLY) 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): def clear_log(self):
self._write_empty(_H2DMsgType.LOG_CLEAR) self._write_empty(_H2DMsgType.LOG_CLEAR)
@ -261,17 +234,15 @@ class CommGeneric:
def flash_storage_read(self, key): def flash_storage_read(self, key):
self._write_header(_H2DMsgType.FLASH_READ_REQUEST) self._write_header(_H2DMsgType.FLASH_READ_REQUEST)
self._write_string(key) self._write_string(key)
self._write_flush()
self._read_header() self._read_header()
self._read_expect(_D2HMsgType.FLASH_READ_REPLY) 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): def flash_storage_write(self, key, value):
self._write_header(_H2DMsgType.FLASH_WRITE_REQUEST) self._write_header(_H2DMsgType.FLASH_WRITE_REQUEST)
self._write_string(key) self._write_string(key)
self._write_bytes(value) self._write_bytes(value)
self._write_flush()
self._read_header() self._read_header()
if self._read_type == _D2HMsgType.FLASH_ERROR_REPLY: if self._read_type == _D2HMsgType.FLASH_ERROR_REPLY:
@ -287,14 +258,12 @@ class CommGeneric:
def flash_storage_remove(self, key): def flash_storage_remove(self, key):
self._write_header(_H2DMsgType.FLASH_REMOVE_REQUEST) self._write_header(_H2DMsgType.FLASH_REMOVE_REQUEST)
self._write_string(key) self._write_string(key)
self._write_flush()
self._read_empty(_D2HMsgType.FLASH_OK_REPLY) self._read_empty(_D2HMsgType.FLASH_OK_REPLY)
def load(self, kernel_library): def load(self, kernel_library):
self._write_header(_H2DMsgType.LOAD_LIBRARY) self._write_header(_H2DMsgType.LOAD_KERNEL)
self._write_chunk(kernel_library) self._write_bytes(kernel_library)
self._write_flush()
self._read_empty(_D2HMsgType.LOAD_COMPLETED) self._read_empty(_D2HMsgType.LOAD_COMPLETED)
@ -442,25 +411,29 @@ class CommGeneric:
raise IOError("Unknown RPC value tag: {}".format(repr(tag))) raise IOError("Unknown RPC value tag: {}".format(repr(tag)))
def _serve_rpc(self, embedding_map): def _serve_rpc(self, embedding_map):
async = self._read_bool()
service_id = self._read_int32() service_id = self._read_int32()
if service_id == 0: args, kwargs = self._receive_rpc_args(embedding_map)
return_tags = self._read_bytes()
if service_id is 0:
service = lambda obj, attr, value: setattr(obj, attr, value) service = lambda obj, attr, value: setattr(obj, attr, value)
else: else:
service = embedding_map.retrieve_object(service_id) service = embedding_map.retrieve_object(service_id)
logger.debug("rpc service: [%d]%r%s %r %r -> %s", service_id, service,
(" (async)" if async else ""), args, kwargs, return_tags)
args, kwargs = self._receive_rpc_args(embedding_map) if async:
return_tags = self._read_bytes() service(*args, **kwargs)
logger.debug("rpc service: [%d]%r %r %r -> %s", service_id, service, args, kwargs, return_tags) return
try: try:
result = service(*args, **kwargs) 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_header(_H2DMsgType.RPC_REPLY)
self._write_bytes(return_tags) self._write_bytes(return_tags)
self._send_rpc_value(bytearray(return_tags), result, result, service) self._send_rpc_value(bytearray(return_tags), result, result, service)
self._write_flush()
except Exception as exn: except Exception as exn:
logger.debug("rpc service: %d %r %r ! %r", service_id, args, kwargs, exn) logger.debug("rpc service: %d %r %r ! %r", service_id, args, kwargs, exn)
@ -503,8 +476,6 @@ class CommGeneric:
self._write_int32(-1) # column not known self._write_int32(-1) # column not known
self._write_string(function) self._write_string(function)
self._write_flush()
def _serve_exception(self, embedding_map, symbolizer, demangler): def _serve_exception(self, embedding_map, symbolizer, demangler):
name = self._read_string() name = self._read_string()
message = self._read_string() message = self._read_string()

View File

@ -29,7 +29,6 @@ def initialize_connection(host, port):
sock.settimeout(None) sock.settimeout(None)
set_keepalive(sock, 3, 2, 3) set_keepalive(sock, 3, 2, 3)
logger.debug("connected to host %s on port %d", host, port) logger.debug("connected to host %s on port %d", host, port)
sock.sendall(b"ARTIQ coredev\n")
return sock return sock
@ -44,6 +43,7 @@ class Comm(CommGeneric):
if hasattr(self, "socket"): if hasattr(self, "socket"):
return return
self.socket = initialize_connection(self.host, self.port) self.socket = initialize_connection(self.host, self.port)
self.socket.sendall(b"ARTIQ coredev\n")
def close(self): def close(self):
if not hasattr(self, "socket"): if not hasattr(self, "socket"):

View File

@ -81,7 +81,7 @@ class Core:
self.core = self self.core = self
self.comm.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: try:
engine = _DiagnosticEngine(all_errors_are_fatal=True) engine = _DiagnosticEngine(all_errors_are_fatal=True)
@ -89,7 +89,9 @@ class Core:
stitcher.stitch_call(function, args, kwargs, set_result) stitcher.stitch_call(function, args, kwargs, set_result)
stitcher.finalize() stitcher.finalize()
module = Module(stitcher, ref_period=self.ref_period) module = Module(stitcher,
ref_period=self.ref_period,
attribute_writeback=attribute_writeback)
target = OR1KTarget() target = OR1KTarget()
library = target.compile_and_link([module]) library = target.compile_and_link([module])
@ -103,6 +105,7 @@ class Core:
def run(self, function, args, kwargs): def run(self, function, args, kwargs):
result = None result = None
@rpc(flags={"async"})
def set_result(new_result): def set_result(new_result):
nonlocal result nonlocal result
result = new_result result = new_result

View File

@ -7,6 +7,7 @@ from PyQt5 import QtCore, QtWidgets
from artiq.tools import short_format from artiq.tools import short_format
from artiq.gui.tools import LayoutWidget, QRecursiveFilterProxyModel from artiq.gui.tools import LayoutWidget, QRecursiveFilterProxyModel
from artiq.gui.models import DictSyncTreeSepModel from artiq.gui.models import DictSyncTreeSepModel
from artiq.gui.scientific_spinbox import ScientificSpinBox
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -50,10 +51,10 @@ class Editor(QtWidgets.QDialog):
class NumberEditor(Editor): class NumberEditor(Editor):
def get_edit_widget(self, initial_value): def get_edit_widget(self, initial_value):
self.edit_widget = QtWidgets.QDoubleSpinBox() self.edit_widget = ScientificSpinBox()
self.edit_widget.setMinimum(float("-inf")) self.edit_widget.setDecimals(13)
self.edit_widget.setMaximum(float("+inf")) self.edit_widget.setPrecision()
self.edit_widget.setDecimals(8) self.edit_widget.setRelativeStep()
self.edit_widget.setValue(float(initial_value)) self.edit_widget.setValue(float(initial_value))
return self.edit_widget return self.edit_widget

View File

@ -23,35 +23,26 @@ _mode_enc = {
} }
class _MoninjWidget(QtWidgets.QFrame): class _TTLWidget(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):
def __init__(self, channel, send_to_device, force_out, title): def __init__(self, channel, send_to_device, force_out, title):
QtWidgets.QFrame.__init__(self)
self.channel = channel self.channel = channel
self.send_to_device = send_to_device self.send_to_device = send_to_device
self.force_out = force_out self.force_out = force_out
_MoninjWidget.__init__(self) self.setFrameShape(QtWidgets.QFrame.Box)
self.setFrameShadow(QtWidgets.QFrame.Raised)
grid = QtWidgets.QGridLayout() grid = QtWidgets.QGridLayout()
grid.setContentsMargins(0, 0, 0, 0)
grid.setHorizontalSpacing(0)
grid.setVerticalSpacing(0)
self.setLayout(grid) self.setLayout(grid)
label = QtWidgets.QLabel(title) label = QtWidgets.QLabel(title)
label.setAlignment(QtCore.Qt.AlignCenter) label.setAlignment(QtCore.Qt.AlignCenter)
label.setWordWrap(True) label.setSizePolicy(QtWidgets.QSizePolicy.Ignored,
QtWidgets.QSizePolicy.Preferred)
grid.addWidget(label, 1, 1) grid.addWidget(label, 1, 1)
self.stack = QtWidgets.QStackedWidget() self.stack = QtWidgets.QStackedWidget()
@ -62,9 +53,18 @@ class _TTLWidget(_MoninjWidget):
self.stack.addWidget(self.direction) self.stack.addWidget(self.direction)
grid_cb = LayoutWidget() 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) 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) grid_cb.addWidget(self.level, 3, 2)
self.stack.addWidget(grid_cb) self.stack.addWidget(grid_cb)
@ -78,19 +78,19 @@ class _TTLWidget(_MoninjWidget):
grid.setRowStretch(4, 1) grid.setRowStretch(4, 1)
self.programmatic_change = False self.programmatic_change = False
self.override.stateChanged.connect(self.override_toggled) self.override.clicked.connect(self.override_toggled)
self.level.stateChanged.connect(self.level_toggled) self.level.clicked.connect(self.level_toggled)
self.set_value(0, False, False) self.set_value(0, False, False)
def enterEvent(self, event): def enterEvent(self, event):
self.stack.setCurrentIndex(1) self.stack.setCurrentIndex(1)
_MoninjWidget.enterEvent(self, event) QtWidgets.QFrame.enterEvent(self, event)
def leaveEvent(self, event): def leaveEvent(self, event):
if not self.override.isChecked(): if not self.override.isChecked():
self.stack.setCurrentIndex(0) self.stack.setCurrentIndex(0)
_MoninjWidget.leaveEvent(self, event) QtWidgets.QFrame.leaveEvent(self, event)
def override_toggled(self, override): def override_toggled(self, override):
if self.programmatic_change: if self.programmatic_change:
@ -125,11 +125,11 @@ class _TTLWidget(_MoninjWidget):
color = " color=\"red\"" color = " color=\"red\""
else: else:
color = "" color = ""
self.value.setText("<font size=\"9\"{}>{}</font>".format( self.value.setText("<font size=\"5\"{}>{}</font>".format(
color, value_s)) color, value_s))
oe = oe or self.force_out oe = oe or self.force_out
direction = "OUT" if oe else "IN" 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 self.programmatic_change = True
try: try:
@ -144,24 +144,30 @@ class _TTLWidget(_MoninjWidget):
return self.channel return self.channel
class _DDSWidget(_MoninjWidget): class _DDSWidget(QtWidgets.QFrame):
def __init__(self, bus_channel, channel, sysclk, title): def __init__(self, bus_channel, channel, sysclk, title):
QtWidgets.QFrame.__init__(self)
self.bus_channel = bus_channel self.bus_channel = bus_channel
self.channel = channel self.channel = channel
self.sysclk = sysclk self.sysclk = sysclk
_MoninjWidget.__init__(self) self.setFrameShape(QtWidgets.QFrame.Box)
self.setFrameShadow(QtWidgets.QFrame.Raised)
grid = QtWidgets.QGridLayout() grid = QtWidgets.QGridLayout()
grid.setContentsMargins(0, 0, 0, 0)
grid.setHorizontalSpacing(0)
grid.setVerticalSpacing(0)
self.setLayout(grid) self.setLayout(grid)
label = QtWidgets.QLabel(title) label = QtWidgets.QLabel(title)
label.setAlignment(QtCore.Qt.AlignCenter) label.setAlignment(QtCore.Qt.AlignCenter)
label.setWordWrap(True) label.setSizePolicy(QtWidgets.QSizePolicy.Ignored,
QtWidgets.QSizePolicy.Preferred)
grid.addWidget(label, 1, 1) grid.addWidget(label, 1, 1)
self.value = QtWidgets.QLabel() self.value = QtWidgets.QLabel()
self.value.setAlignment(QtCore.Qt.AlignCenter) self.value.setAlignment(QtCore.Qt.AlignCenter)
self.value.setWordWrap(True)
grid.addWidget(self.value, 2, 1, 6, 1) grid.addWidget(self.value, 2, 1, 6, 1)
grid.setRowStretch(1, 1) grid.setRowStretch(1, 1)
@ -172,7 +178,7 @@ class _DDSWidget(_MoninjWidget):
def set_value(self, ftw): def set_value(self, ftw):
frequency = ftw*self.sysclk()/2**32 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)) .format(frequency/1e6))
def sort_key(self): def sort_key(self):

View File

@ -13,6 +13,17 @@ logger = logging.getLogger(__name__)
class Segment: 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_time = 1 << 16 # uint16 timer
max_val = 1 << 15 # int16 DAC max_val = 1 << 15 # int16 DAC
max_out = 10. # Volt max_out = 10. # Volt
@ -27,6 +38,24 @@ class Segment:
def line(self, typ, duration, data, trigger=False, silence=False, def line(self, typ, duration, data, trigger=False, silence=False,
aux=False, shift=0, jump=False, clear=False, wait=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 == 0, data
assert len(data)//2 <= 14 assert len(data)//2 <= 14
# assert dt*(1 << shift) > 1 + len(data)//2 # assert dt*(1 << shift) > 1 + len(data)//2
@ -39,6 +68,15 @@ class Segment:
@staticmethod @staticmethod
def pack(widths, values): 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 = "<" fmt = "<"
ud = [] ud = []
for width, value in zip(widths, values): for width, value in zip(widths, values):
@ -60,7 +98,11 @@ class Segment:
def bias(self, amplitude=[], **kwargs): def bias(self, amplitude=[], **kwargs):
"""Append a bias line to this segment. """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] coef = [self.out_scale*a for a in amplitude]
discrete_compensate(coef) discrete_compensate(coef)
@ -68,12 +110,18 @@ class Segment:
self.line(typ=0, data=data, **kwargs) self.line(typ=0, data=data, **kwargs)
def dds(self, amplitude=[], phase=[], **kwargs): def dds(self, amplitude=[], phase=[], **kwargs):
"""Append a dds line to this segment. """Append a DDS line to this segment.
Amplitude in volts, Args:
phase[0] in turns, amplitude (list[float]): Amplitude coefficients in in Volts and
phase[1] in turns*sample_rate, increasing powers of ``1/(2**shift*clock_period)``.
phase[2] in turns*(sample_rate/2**shift)**2 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 scale = self.out_scale/self.cordic_gain
coef = [scale*a for a in amplitude] coef = [scale*a for a in amplitude]
@ -86,6 +134,13 @@ class Segment:
class Channel: 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 num_frames = 8
max_data = 4*(1 << 10) # 8kx16 8kx16 4kx16 max_data = 4*(1 << 10) # 8kx16 8kx16 4kx16
@ -93,14 +148,27 @@ class Channel:
self.segments = [] self.segments = []
def clear(self): def clear(self):
"""Remove all segments."""
self.segments.clear() self.segments.clear()
def new_segment(self): def new_segment(self):
"""Create and attach a new :class:`Segment` to this channel.
Returns:
:class:`Segment`
"""
segment = Segment() segment = Segment()
self.segments.append(segment) self.segments.append(segment)
return segment return segment
def place(self): 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 addr = self.num_frames
for segment in self.segments: for segment in self.segments:
segment.addr = addr segment.addr = addr
@ -109,6 +177,23 @@ class Channel:
return addr return addr
def table(self, entry=None): 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 table = [0] * self.num_frames
if entry is None: if entry is None:
entry = self.segments entry = self.segments
@ -118,6 +203,18 @@ class Channel:
return struct.pack("<" + "H"*self.num_frames, *table) return struct.pack("<" + "H"*self.num_frames, *table)
def serialize(self, entry=None): 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() self.place()
data = b"".join([segment.data for segment in self.segments]) data = b"".join([segment.data for segment in self.segments])
return self.table(entry) + data return self.table(entry) + data
@ -125,7 +222,22 @@ class Channel:
class Pdq2: 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 num_dacs = 3
freq = 50e6 freq = 50e6
@ -154,42 +266,58 @@ class Pdq2:
self.freq = float(freq) self.freq = float(freq)
def close(self): def close(self):
"""Close the USB device handle."""
self.dev.close() self.dev.close()
del self.dev del self.dev
def write(self, data): def write(self, data):
"""Write data to the PDQ2 board.
Args:
data (bytes): Data to write.
"""
logger.debug("> %r", data) logger.debug("> %r", data)
written = self.dev.write(data) written = self.dev.write(data)
if isinstance(written, int): if isinstance(written, int):
assert written == len(data) assert written == len(data)
def cmd(self, cmd, enable): 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 cmd = self._commands.index(cmd) << 1
if not enable: if not enable:
cmd |= 1 cmd |= 1
self.write(struct.pack("cb", self._escape, cmd)) self.write(struct.pack("cb", self._escape, cmd))
def write_mem(self, channel, data, start_addr=0): 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) board, dac = divmod(channel, self.num_dacs)
data = struct.pack("<HHH", (board << 4) | dac, start_addr, data = struct.pack("<HHH", (board << 4) | dac, start_addr,
start_addr + len(data)//2 - 1) + data start_addr + len(data)//2 - 1) + data
data = data.replace(self._escape, self._escape + self._escape) data = data.replace(self._escape, self._escape + self._escape)
self.write(data) 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): 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): for i, line in enumerate(data):
dac_divider = line.get("dac_divider", 1) dac_divider = line.get("dac_divider", 1)
shift = int(log(dac_divider, 2)) shift = int(log(dac_divider, 2))
@ -208,6 +336,25 @@ class Pdq2:
silence=silence, **target_data) silence=silence, **target_data)
def program(self, program, channels=None): 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: if channels is None:
channels = range(self.num_channels) channels = range(self.num_channels)
chs = [self.channels[i] for i in channels] chs = [self.channels[i] for i in channels]
@ -225,5 +372,18 @@ class Pdq2:
for channel, ch in zip(channels, chs): for channel, ch in zip(channels, chs):
self.write_mem(channel, ch.serialize()) 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): def ping(self):
return True return True

View File

@ -52,14 +52,13 @@ class FloppingF(EnvExperiment):
self.mutate_dataset("flopping_f_frequency", i, f) self.mutate_dataset("flopping_f_frequency", i, f)
self.mutate_dataset("flopping_f_brightness", i, m_brightness) self.mutate_dataset("flopping_f_brightness", i, m_brightness)
time.sleep(0.1) time.sleep(0.1)
self.scheduler.submit(self.scheduler.pipeline_name, self.scheduler.expid, self.scheduler.submit(due_date=time.time() + 20)
self.scheduler.priority, time.time() + 20, False)
def analyze(self): def analyze(self):
# Use get_dataset so that analyze can be run stand-alone. # Use get_dataset so that analyze can be run stand-alone.
brightness = self.get_dataset("flopping_f_brightness") brightness = self.get_dataset("flopping_f_brightness")
try: try:
frequency = self.get_dataset("flopping_f_frequency") frequency = self.get_dataset("flopping_f_frequency", archive=False)
except KeyError: except KeyError:
# Since flopping_f_frequency is not saved, it is missing if # Since flopping_f_frequency is not saved, it is missing if
# analyze() is run on HDF5 data. But assuming that the arguments # 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, self.set_dataset("flopping_f_frequency", frequency,
broadcast=True, save=False) broadcast=True, save=False)
popt, pcov = curve_fit(model, frequency, brightness, 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)) perr = np.sqrt(np.diag(pcov))
if perr < 0.1: if perr < 0.1:
F0 = float(popt) F0 = float(popt)

View File

@ -55,7 +55,7 @@ def main():
object_map, kernel_library, _, _ = \ object_map, kernel_library, _, _ = \
core.compile(exp.run, [exp_inst], {}, core.compile(exp.run, [exp_inst], {},
with_attr_writeback=False) attribute_writeback=False)
except CompileError as error: except CompileError as error:
return return
finally: finally:

View 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()

View File

@ -73,8 +73,7 @@ class LLVMIRRunner(FileRunner):
with open(self.file, "r") as f: with open(self.file, "r") as f:
llmodule = llvm.parse_assembly(f.read()) llmodule = llvm.parse_assembly(f.read())
llmodule.verify() llmodule.verify()
return self.target.link([self.target.assemble(llmodule)], return self.target.link([self.target.assemble(llmodule)])
init_fn="__modinit__")
class LLVMBitcodeRunner(FileRunner): class LLVMBitcodeRunner(FileRunner):
@ -82,8 +81,7 @@ class LLVMBitcodeRunner(FileRunner):
with open(self.file, "rb") as f: with open(self.file, "rb") as f:
llmodule = llvm.parse_bitcode(f.read()) llmodule = llvm.parse_bitcode(f.read())
llmodule.verify() llmodule.verify()
return self.target.link([self.target.assemble(llmodule)], return self.target.link([self.target.assemble(llmodule)])
init_fn="__modinit__")
class DummyScheduler: class DummyScheduler:
@ -95,7 +93,7 @@ class DummyScheduler:
self._next_rid = 1 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 rid = self._next_rid
self._next_rid += 1 self._next_rid += 1
logger.info("Submitting: %s, RID=%s", expid, rid) logger.info("Submitting: %s, RID=%s", expid, rid)

View File

@ -7,7 +7,7 @@ from misoc.integration.soc_core import mem_decoder
class KernelCPU(Module): class KernelCPU(Module):
def __init__(self, platform, def __init__(self, platform,
exec_address=0x42000000, exec_address=0x40800000,
main_mem_origin=0x40000000, main_mem_origin=0x40000000,
l2_size=8192): l2_size=8192):
self._reset = CSRStorage(reset=1) self._reset = CSRStorage(reset=1)

View File

@ -3,19 +3,19 @@ from misoc.interconnect import wishbone
class Mailbox(Module): class Mailbox(Module):
def __init__(self): def __init__(self, size=1):
self.i1 = wishbone.Interface() self.i1 = wishbone.Interface()
self.i2 = wishbone.Interface() self.i2 = wishbone.Interface()
# # # # # #
value = Signal(32) values = Array([Signal(32) for _ in range(size)])
for i in self.i1, self.i2: for i in self.i1, self.i2:
self.sync += [ self.sync += [
i.dat_r.eq(value), i.dat_r.eq(values[i.adr[:bits_for(size-1)]]),
i.ack.eq(0), i.ack.eq(0),
If(i.cyc & i.stb & ~i.ack, If(i.cyc & i.stb & ~i.ack,
i.ack.eq(1), 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))
) )
] ]

View File

@ -1,7 +1,7 @@
from migen import * from migen import *
from misoc.interconnect.stream import Endpoint from misoc.interconnect.stream import Endpoint
from misoc.cores.cordic import Cordic from misoc.cores.cordic import Cordic
from .accu import PhasedAccu, Accu from .accu import PhasedAccu, Accu
from .tools import eqh, Delay from .tools import eqh, Delay
from .spline import Spline from .spline import Spline

View File

@ -13,10 +13,10 @@ fmc_adapter_io = [
Subsignal("mosi", Pins("HPC:LA03_N")), Subsignal("mosi", Pins("HPC:LA03_N")),
Subsignal("miso", Pins("HPC:LA04_P")), Subsignal("miso", Pins("HPC:LA04_P")),
Subsignal("en", Pins("HPC:LA05_N")), Subsignal("en", Pins("HPC:LA05_N")),
IOStandard("LVTTL"), IOStandard("LVCMOS25"),
), ),
("ad9154_txen", 0, Pins("HPC:LA07_P"), IOStandard("LVTTL")), ("ad9154_txen", 0, Pins("HPC:LA07_P"), IOStandard("LVCMOS25")),
("ad9154_txen", 1, Pins("HPC:LA07_N"), IOStandard("LVTTL")), ("ad9154_txen", 1, Pins("HPC:LA07_N"), IOStandard("LVCMOS25")),
("ad9154_refclk", 0, ("ad9154_refclk", 0,
Subsignal("p", Pins("HPC:GBTCLK0_M2C_P")), Subsignal("p", Pins("HPC:GBTCLK0_M2C_P")),
Subsignal("n", Pins("HPC:GBTCLK0_M2C_N")), Subsignal("n", Pins("HPC:GBTCLK0_M2C_N")),
@ -25,16 +25,19 @@ fmc_adapter_io = [
Subsignal("p", Pins("HPC:LA00_CC_P")), Subsignal("p", Pins("HPC:LA00_CC_P")),
Subsignal("n", Pins("HPC:LA00_CC_N")), Subsignal("n", Pins("HPC:LA00_CC_N")),
IOStandard("LVDS_25"), IOStandard("LVDS_25"),
Misc("DIFF_TERM=TRUE"),
), ),
("ad9154_sync", 0, ("ad9154_sync", 0,
Subsignal("p", Pins("HPC:LA01_CC_P")), Subsignal("p", Pins("HPC:LA01_CC_P")),
Subsignal("n", Pins("HPC:LA01_CC_N")), Subsignal("n", Pins("HPC:LA01_CC_N")),
IOStandard("LVDS_25"), IOStandard("LVDS_25"),
Misc("DIFF_TERM=TRUE"),
), ),
("ad9154_sync", 1, ("ad9154_sync", 1,
Subsignal("p", Pins("HPC:LA02_P")), Subsignal("p", Pins("HPC:LA02_P")),
Subsignal("n", Pins("HPC:LA02_N")), Subsignal("n", Pins("HPC:LA02_N")),
IOStandard("LVDS_25"), IOStandard("LVDS_25"),
Misc("DIFF_TERM=TRUE"),
), ),
("ad9154_jesd", 0, # AD9154's SERIND7 ("ad9154_jesd", 0, # AD9154's SERIND7
Subsignal("txp", Pins("HPC:DP0_C2M_P")), Subsignal("txp", Pins("HPC:DP0_C2M_P")),

View File

@ -24,10 +24,8 @@ class _GrayCodeTransfer(Module):
self.sync.rtio += value_gray_rtio.eq(self.i ^ self.i[1:]) self.sync.rtio += value_gray_rtio.eq(self.i ^ self.i[1:])
# transfer to system clock domain # transfer to system clock domain
value_gray_sys = Signal(width) value_gray_sys = Signal(width)
self.specials += [ value_gray_rtio.attr.add("no_retiming")
NoRetiming(value_gray_rtio), self.specials += MultiReg(value_gray_rtio, value_gray_sys)
MultiReg(value_gray_rtio, value_gray_sys)
]
# convert back to binary # convert back to binary
value_sys = Signal(width) value_sys = Signal(width)
self.comb += value_sys[-1].eq(value_gray_sys[-1]) self.comb += value_sys[-1].eq(value_gray_sys[-1])

View File

@ -28,7 +28,7 @@ class AMPSoC:
self.add_cpulevel_sdram_if(self.kernel_cpu.wb_sdram) self.add_cpulevel_sdram_if(self.kernel_cpu.wb_sdram)
self.csr_devices.append("kernel_cpu") 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.add_wb_slave(mem_decoder(self.mem_map["mailbox"]),
self.mailbox.i1) self.mailbox.i1)
self.kernel_cpu.add_wb_slave(mem_decoder(self.mem_map["mailbox"]), self.kernel_cpu.add_wb_slave(mem_decoder(self.mem_map["mailbox"]),

View File

@ -8,7 +8,6 @@ from migen.genlib.cdc import MultiReg
from migen.build.generic_platform import * from migen.build.generic_platform import *
from migen.build.xilinx.vivado import XilinxVivadoToolchain from migen.build.xilinx.vivado import XilinxVivadoToolchain
from migen.build.xilinx.ise import XilinxISEToolchain from migen.build.xilinx.ise import XilinxISEToolchain
from migen.fhdl.specials import Keep
from migen.genlib.io import DifferentialInput from migen.genlib.io import DifferentialInput
from jesd204b.common import (JESD204BTransportSettings, from jesd204b.common import (JESD204BTransportSettings,
@ -149,10 +148,10 @@ class _NIST_Ions(MiniSoC, AMPSoC):
self.register_kernel_cpu_csrdevice("i2c") self.register_kernel_cpu_csrdevice("i2c")
self.config["I2C_BUS_COUNT"] = 1 self.config["I2C_BUS_COUNT"] = 1
def add_rtio(self, rtio_channels, rtio_crg=None): self.config["HAS_DDS"] = None
if rtio_crg is None:
rtio_crg = _RTIOCRG(self.platform, self.crg.cd_sys.clk) def add_rtio(self, rtio_channels):
self.submodules.rtio_crg = rtio_crg self.submodules.rtio_crg = _RTIOCRG(self.platform, self.crg.cd_sys.clk)
self.csr_devices.append("rtio_crg") self.csr_devices.append("rtio_crg")
self.submodules.rtio = rtio.RTIO(rtio_channels) self.submodules.rtio = rtio.RTIO(rtio_channels)
self.register_kernel_cpu_csrdevice("rtio") self.register_kernel_cpu_csrdevice("rtio")
@ -160,22 +159,11 @@ class _NIST_Ions(MiniSoC, AMPSoC):
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels) self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
self.csr_devices.append("rtio_moninj") self.csr_devices.append("rtio_moninj")
self.specials += [ self.rtio_crg.cd_rtio.clk.attr.add("keep")
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.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.) 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.platform.add_false_path_constraints(
self.rtio.cd_rsys.clk, self.crg.cd_sys.clk,
self.rtio_crg.cd_rtio.clk, self.rtio_crg.cd_rtio.clk)
self.ethphy.crg.cd_eth_rx.clk,
self.ethphy.crg.cd_eth_tx.clk)
self.submodules.rtio_analyzer = rtio.Analyzer(self.rtio, self.submodules.rtio_analyzer = rtio.Analyzer(self.rtio,
self.get_native_sdram_if()) 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_FIRST_DDS_CHANNEL"] = len(rtio_channels)
self.config["RTIO_DDS_COUNT"] = 1 self.config["RTIO_DDS_COUNT"] = 1
self.config["DDS_CHANNELS_PER_BUS"] = 8 self.config["DDS_CHANNELS_PER_BUS"] = 8
self.config["DDS_AD9858"] = True self.config["DDS_AD9858"] = None
phy = dds.AD9858(platform.request("dds"), 8) phy = dds.AD9858(platform.request("dds"), 8)
self.submodules += phy self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(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_FIRST_DDS_CHANNEL"] = len(rtio_channels)
self.config["RTIO_DDS_COUNT"] = 1 self.config["RTIO_DDS_COUNT"] = 1
self.config["DDS_CHANNELS_PER_BUS"] = 11 self.config["DDS_CHANNELS_PER_BUS"] = 11
self.config["DDS_AD9914"] = True self.config["DDS_AD9914"] = None
self.config["DDS_ONEHOT_SEL"] = True self.config["DDS_ONEHOT_SEL"] = None
phy = dds.AD9914(platform.request("dds"), 11, onehot=True) phy = dds.AD9914(platform.request("dds"), 11, onehot=True)
self.submodules += phy self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(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_FIRST_DDS_CHANNEL"] = len(rtio_channels)
self.config["RTIO_DDS_COUNT"] = 2 self.config["RTIO_DDS_COUNT"] = 2
self.config["DDS_CHANNELS_PER_BUS"] = 12 self.config["DDS_CHANNELS_PER_BUS"] = 12
self.config["DDS_AD9914"] = True self.config["DDS_AD9914"] = None
self.config["DDS_ONEHOT_SEL"] = True self.config["DDS_ONEHOT_SEL"] = None
for backplane_offset in range(2): for backplane_offset in range(2):
phy = dds.AD9914( phy = dds.AD9914(
platform.request("dds", backplane_offset), 12, onehot=True) platform.request("dds", backplane_offset), 12, onehot=True)
@ -394,11 +382,10 @@ class NIST_QC2(_NIST_Ions):
class _PhaserCRG(Module, AutoCSR): class _PhaserCRG(Module, AutoCSR):
def __init__(self, platform, rtio_internal_clk): def __init__(self, platform, refclk):
self._clock_sel = CSRStorage() self._clock_sel = CSRStorage()
self._pll_reset = CSRStorage(reset=1) self._pll_reset = CSRStorage(reset=1)
self._pll_locked = CSRStatus() self._pll_locked = CSRStatus()
self.refclk = Signal()
self.clock_domains.cd_rtio = ClockDomain() self.clock_domains.cd_rtio = ClockDomain()
self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True) 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_REF_JITTER1=0.01, p_REF_JITTER2=0.01,
p_CLKIN1_PERIOD=4.0, p_CLKIN2_PERIOD=4.0, 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 # Warning: CLKINSEL=0 means CLKIN2 is selected
i_CLKINSEL=~self._clock_sel.storage, 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, p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=2,
i_CLKFBIN=self.cd_rtio.clk, i_CLKFBIN=self.cd_rtio.clk,
i_RST=self._pll_reset.storage, i_RST=self._pll_reset.storage,
@ -431,6 +418,8 @@ class _PhaserCRG(Module, AutoCSR):
MultiReg(pll_locked | ~self._clock_sel.storage, MultiReg(pll_locked | ~self._clock_sel.storage,
self._pll_locked.status) self._pll_locked.status)
] ]
self.cd_rtio.clk.attr.add("keep")
platform.add_period_constraint(self.cd_rtio.clk, 8.)
class AD9154JESD(Module, AutoCSR): class AD9154JESD(Module, AutoCSR):
@ -444,28 +433,29 @@ class AD9154JESD(Module, AutoCSR):
sync_pads = platform.request("ad9154_sync") sync_pads = platform.request("ad9154_sync")
self.jsync = Signal() self.jsync = Signal()
self.refclk = Signal()
self.specials += DifferentialInput( self.specials += DifferentialInput(
sync_pads.p, sync_pads.n, self.jsync) sync_pads.p, sync_pads.n, self.jsync)
refclk = Signal()
self.clock_domains.cd_jesd = ClockDomain() self.clock_domains.cd_jesd = ClockDomain()
refclk_pads = platform.request("ad9154_refclk") refclk_pads = platform.request("ad9154_refclk")
platform.add_period_constraint(refclk_pads.p, 1e9/refclk_freq)
self.specials += [ self.specials += [
Instance("IBUFDS_GTE2", i_CEB=0, Instance("IBUFDS_GTE2", i_CEB=0,
i_I=refclk_pads.p, i_IB=refclk_pads.n, o_O=self.refclk), i_I=refclk_pads.p, i_IB=refclk_pads.n, o_O=refclk),
Instance("BUFG", i_I=self.refclk, o_O=self.cd_jesd.clk), Instance("BUFG", i_I=refclk, o_O=self.cd_jesd.clk),
AsyncResetSynchronizer(self.cd_jesd, ResetSignal("rio_phy")), 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) 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 self.submodules += qpll
phys = [] phys = []
for i in range(4): for i in range(4):
phy = JESD204BPhyTX( phy = JESD204BPhyTX(
qpll, platform.request("ad9154_jesd", i), fabric_freq) 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_period_constraint(phy.gtx.cd_tx.clk, 40*1e9/linerate)
platform.add_false_path_constraints(self.cd_jesd.clk, platform.add_false_path_constraints(self.cd_jesd.clk,
phy.gtx.cd_tx.clk) phy.gtx.cd_tx.clk)
@ -484,16 +474,15 @@ class AD9154JESD(Module, AutoCSR):
# blinking leds for transceiver reset status # blinking leds for transceiver reset status
for i in range(4): for i in range(4):
led = platform.request("user_led", 4 + i) counter = Signal(max=fabric_freq)
counter = Signal(max=fabric_freq//2 + 1) self.comb += platform.request("user_led", 4 + i).eq(counter[-1])
sync = getattr(self.sync, "phy{}_tx".format(i)) sync = getattr(self.sync, "phy{}_tx".format(i))
sync += \ sync += [
counter.eq(counter - 1),
If(counter == 0, If(counter == 0,
led.eq(~led), counter.eq(fabric_freq - 1)
counter.eq(fabric_freq//2)
).Else(
counter.eq(counter - 1)
) )
]
class AD9154(Module, AutoCSR): 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:]))) 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 = { mem_map = {
"timer_kernel": 0x10000000, # (shadow @0x90000000)
"rtio": 0x20000000, # (shadow @0xa0000000)
"i2c": 0x30000000, # (shadow @0xb0000000)
"mailbox": 0x70000000, # (shadow @0xf0000000)
"ad9154": 0x50000000, "ad9154": 0x50000000,
} }
mem_map.update(_NIST_Ions.mem_map) mem_map.update(MiniSoC.mem_map)
def __init__(self, cpu_type="or1k", **kwargs): 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 = self.platform
platform.add_extension(_ams101_dac)
platform.add_extension(phaser.fmc_adapter_io) 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.submodules.ad9154 = AD9154(platform)
self.register_kernel_cpu_csrdevice("ad9154") self.register_kernel_cpu_csrdevice("ad9154")
self.config["AD9154_DAC_CS"] = 1 << 0 self.config["AD9154_DAC_CS"] = 1 << 0
@ -537,7 +551,7 @@ class Phaser(_NIST_Ions):
rtio_channels = [] rtio_channels = []
phy = ttl_serdes_7series.Inout_8X( phy = ttl_serdes_7series.Inout_8X(
platform.request("user_sma_gpio_n_33")) platform.request("user_sma_gpio_n"))
self.submodules += phy self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=128)) 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) self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
rtio_channels.append(rtio.LogChannel()) 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(): def main():

View File

@ -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( rtio_channels.append(rtio.Channel.from_phy(
phy, ofifo_depth=64, ififo_depth=64)) phy, ofifo_depth=64, ififo_depth=64))
self.config["HAS_DDS"] = None
self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels) self.config["RTIO_FIRST_DDS_CHANNEL"] = len(rtio_channels)
self.config["RTIO_DDS_COUNT"] = 1 self.config["RTIO_DDS_COUNT"] = 1
self.config["DDS_CHANNELS_PER_BUS"] = 8 self.config["DDS_CHANNELS_PER_BUS"] = 8
self.config["DDS_AD9858"] = True self.config["DDS_AD9858"] = None
dds_pins = platform.request("dds") dds_pins = platform.request("dds")
self.comb += dds_pins.p.eq(0) self.comb += dds_pins.p.eq(0)
phy = dds.AD9858(dds_pins, 8) phy = dds.AD9858(dds_pins, 8)

View File

@ -7,7 +7,7 @@ from functools import wraps
import numpy import numpy
__all__ = ["kernel", "portable", "syscall", "host_only", __all__ = ["kernel", "portable", "rpc", "syscall", "host_only",
"set_time_manager", "set_watchdog_factory", "set_time_manager", "set_watchdog_factory",
"TerminationRequested"] "TerminationRequested"]
@ -22,7 +22,7 @@ __all__.extend(kernel_globals)
_ARTIQEmbeddedInfo = namedtuple("_ARTIQEmbeddedInfo", _ARTIQEmbeddedInfo = namedtuple("_ARTIQEmbeddedInfo",
"core_name function syscall forbidden flags") "core_name portable function syscall forbidden flags")
def kernel(arg=None, flags={}): def kernel(arg=None, flags={}):
""" """
@ -53,7 +53,7 @@ def kernel(arg=None, flags={}):
def run_on_core(self, *k_args, **k_kwargs): def run_on_core(self, *k_args, **k_kwargs):
return getattr(self, arg).run(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( 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)) forbidden=False, flags=set(flags))
return run_on_core return run_on_core
return inner_decorator return inner_decorator
@ -83,7 +83,23 @@ def portable(arg=None, flags={}):
return inner_decorator return inner_decorator
else: else:
arg.artiq_embedded = \ 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)) forbidden=False, flags=set(flags))
return arg return arg
@ -101,7 +117,7 @@ def syscall(arg=None, flags={}):
if isinstance(arg, str): if isinstance(arg, str):
def inner_decorator(function): def inner_decorator(function):
function.artiq_embedded = \ function.artiq_embedded = \
_ARTIQEmbeddedInfo(core_name=None, function=None, _ARTIQEmbeddedInfo(core_name=None, portable=False, function=None,
syscall=function.__name__, forbidden=False, syscall=function.__name__, forbidden=False,
flags=set(flags)) flags=set(flags))
return function return function
@ -119,7 +135,7 @@ def host_only(function):
in the host Python interpreter. in the host Python interpreter.
""" """
function.artiq_embedded = \ function.artiq_embedded = \
_ARTIQEmbeddedInfo(core_name=None, function=None, syscall=None, _ARTIQEmbeddedInfo(core_name=None, portable=False, function=None, syscall=None,
forbidden=True, flags={}) forbidden=True, flags={})
return function return function

View File

@ -3,6 +3,7 @@ from inspect import isclass
from artiq.protocols import pyon from artiq.protocols import pyon
from artiq.language import units from artiq.language import units
from artiq.language.core import rpc
__all__ = ["NoDefault", __all__ = ["NoDefault",
@ -274,6 +275,7 @@ class HasEnvironment:
kernel_invariants = getattr(self, "kernel_invariants", set()) kernel_invariants = getattr(self, "kernel_invariants", set())
self.kernel_invariants = kernel_invariants | {key} self.kernel_invariants = kernel_invariants | {key}
@rpc(flags={"async"})
def set_dataset(self, key, value, def set_dataset(self, key, value,
broadcast=False, persist=False, save=True): broadcast=False, persist=False, save=True):
"""Sets the contents and handling modes of a dataset. """Sets the contents and handling modes of a dataset.
@ -290,6 +292,7 @@ class HasEnvironment:
""" """
self.__dataset_mgr.set(key, value, broadcast, persist, save) self.__dataset_mgr.set(key, value, broadcast, persist, save)
@rpc(flags={"async"})
def mutate_dataset(self, key, index, value): def mutate_dataset(self, key, index, value):
"""Mutate an existing dataset at the given index (e.g. set a value at """Mutate an existing dataset at the given index (e.g. set a value at
a given position in a NumPy array) a given position in a NumPy array)
@ -303,7 +306,7 @@ class HasEnvironment:
as ``slice(*sub_tuple)`` (multi-dimensional slicing).""" as ``slice(*sub_tuple)`` (multi-dimensional slicing)."""
self.__dataset_mgr.mutate(key, index, value) 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. """Returns the contents of a dataset.
The local storage is searched first, followed by the master storage 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 If the dataset does not exist, returns the default value. If no default
is provided, raises ``KeyError``. 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: try:
return self.__dataset_mgr.get(key) return self.__dataset_mgr.get(key, archive)
except KeyError: except KeyError:
if default is NoDefault: if default is NoDefault:
raise raise
else: else:
return default 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 """Sets the contents of a dataset as attribute. The names of the
dataset and of the attribute are the same.""" 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: class Experiment:

View File

@ -6,7 +6,7 @@ annotations.
from artiq.compiler import types, builtins from artiq.compiler import types, builtins
__all__ = ["TNone", "TBool", "TInt32", "TInt64", "TFloat", __all__ = ["TNone", "TBool", "TInt32", "TInt64", "TFloat",
"TStr", "TList", "TRange32", "TRange64", "TVar"] "TStr", "TTuple", "TList", "TRange32", "TRange64", "TVar"]
TNone = builtins.TNone() TNone = builtins.TNone()
TBool = builtins.TBool() TBool = builtins.TBool()
@ -14,6 +14,7 @@ TInt32 = builtins.TInt(types.TValue(32))
TInt64 = builtins.TInt(types.TValue(64)) TInt64 = builtins.TInt(types.TValue(64))
TFloat = builtins.TFloat() TFloat = builtins.TFloat()
TStr = builtins.TStr() TStr = builtins.TStr()
TTuple = types.TTuple
TList = builtins.TList TList = builtins.TList
TRange32 = builtins.TRange(builtins.TInt(types.TValue(32))) TRange32 = builtins.TRange(builtins.TInt(types.TValue(32)))
TRange64 = builtins.TRange(builtins.TInt(types.TValue(64))) TRange64 = builtins.TRange(builtins.TInt(types.TValue(64)))

View File

@ -18,3 +18,4 @@ _register_unit("s", "pnum_")
_register_unit("Hz", "_kMG") _register_unit("Hz", "_kMG")
_register_unit("dB", "_") _register_unit("dB", "_")
_register_unit("V", "um_k") _register_unit("V", "um_k")
_register_unit("A", "um_")

View File

@ -402,8 +402,12 @@ class Scheduler:
if self._pipelines: if self._pipelines:
logger.warning("some pipelines were not garbage-collected") logger.warning("some pipelines were not garbage-collected")
def submit(self, pipeline_name, expid, priority, due_date, flush): def submit(self, pipeline_name, expid, priority=0, due_date=None, flush=False):
"""Submits a new run.""" """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 # mutates expid to insert head repository revision if None
if self._terminated: if self._terminated:
return return
@ -446,6 +450,9 @@ class Scheduler:
whether returning control to the host and pausing would have an effect, 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 in order to avoid the cost of switching kernels in the common case
where ``pause`` does nothing. 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(): for pipeline in self._pipelines.values():
if rid in pipeline.pool.runs: if rid in pipeline.pool.runs:

View File

@ -181,23 +181,35 @@ class DatasetManager:
def __init__(self, ddb): def __init__(self, ddb):
self.broadcast = Notifier(dict()) self.broadcast = Notifier(dict())
self.local = dict() self.local = dict()
self.archive = dict()
self.ddb = ddb self.ddb = ddb
self.broadcast.publish = ddb.update self.broadcast.publish = ddb.update
def set(self, key, value, broadcast=False, persist=False, save=True): 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: if persist:
broadcast = True broadcast = True
if broadcast: if broadcast:
self.broadcast[key] = persist, value self.broadcast[key] = persist, value
elif key in self.broadcast.read:
del self.broadcast[key]
if save: if save:
self.local[key] = value self.local[key] = value
elif key in self.local:
del self.local[key]
def mutate(self, key, index, value): def mutate(self, key, index, value):
target = None target = None
if key in self.local: if key in self.local:
target = self.local[key] target = self.local[key]
if key in self.broadcast.read: if key in self.broadcast.read:
if target is not None:
assert target is self.broadcast.read[key][1]
target = self.broadcast[key][1] target = self.broadcast[key][1]
if target is None: if target is None:
raise KeyError("Cannot mutate non-existing dataset") raise KeyError("Cannot mutate non-existing dataset")
@ -209,12 +221,22 @@ class DatasetManager:
index = slice(*index) index = slice(*index)
setitem(target, index, value) setitem(target, index, value)
def get(self, key): def get(self, key, archive=False):
if key in self.local: if key in self.local:
return self.local[key] return self.local[key]
else: 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): def write_hdf5(self, f):
datasets_group = f.create_group("datasets")
for k, v in self.local.items(): 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

View File

@ -79,32 +79,39 @@ set_watchdog_factory(Watchdog)
class Scheduler: 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): def set_run_info(self, rid, pipeline_name, expid, priority):
self.rid = rid self.rid = rid
self.pipeline_name = pipeline_name self.pipeline_name = pipeline_name
self.expid = expid self.expid = expid
self.priority = priority 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: def check_pause(self, rid=None) -> TBool:
if rid is None: if rid is None:
rid = self.rid rid = self.rid
return self._check_pause(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: class CCB:
issue = staticmethod(make_parent_action("ccb_issue")) issue = staticmethod(make_parent_action("ccb_issue"))
@ -238,7 +245,7 @@ def main():
elif action == "write_results": elif action == "write_results":
filename = "{:09}-{}.h5".format(rid, exp.__name__) filename = "{:09}-{}.h5".format(rid, exp.__name__)
with h5py.File(filename, "w") as f: 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["artiq_version"] = artiq_version
f["rid"] = rid f["rid"] = rid
f["start_time"] = int(time.mktime(start_time)) f["start_time"] = int(time.mktime(start_time))

View File

@ -2,15 +2,24 @@
name = "runtime" name = "runtime"
version = "0.0.0" version = "0.0.0"
dependencies = [ 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)", "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", "lwip 0.0.0",
"std_artiq 0.0.0", "std_artiq 0.0.0",
"walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "alloc_artiq" name = "alloc_artiq"
version = "0.0.0" version = "0.0.0"
[[package]]
name = "byteorder"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "fringe" name = "fringe"
version = "1.1.0" version = "1.1.0"
@ -19,11 +28,30 @@ dependencies = [
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "libc" name = "libc"
version = "0.2.15" version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "lwip" name = "lwip"
version = "0.0.0" version = "0.0.0"
@ -43,6 +71,32 @@ dependencies = [
"alloc_artiq 0.0.0", "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] [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 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 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"

View File

@ -2,16 +2,23 @@
authors = ["The ARTIQ Project Developers"] authors = ["The ARTIQ Project Developers"]
name = "runtime" name = "runtime"
version = "0.0.0" version = "0.0.0"
build = "build.rs"
[build-dependencies]
walkdir = "0.1"
[lib] [lib]
name = "artiq_rust" name = "runtime"
crate-type = ["staticlib"] crate-type = ["staticlib"]
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [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"] } 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] [profile.dev]
panic = 'abort' panic = 'abort'

42
artiq/runtime.rs/build.rs Normal file
View 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
View 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"

View 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

View 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
];

View 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
}
}

View 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)
}

View File

@ -103,6 +103,10 @@ pub struct udp_pcb {
pub const TCP_WRITE_FLAG_COPY: u8 = 0x01; pub const TCP_WRITE_FLAG_COPY: u8 = 0x01;
pub const TCP_WRITE_FLAG_MORE: u8 = 0x02; 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 { extern {
pub fn pbuf_alloc(l: pbuf_layer, length: u16, type_: pbuf_type) -> *mut pbuf; pub fn pbuf_alloc(l: pbuf_layer, length: u16, type_: pbuf_type) -> *mut pbuf;
pub fn pbuf_realloc(p: *mut pbuf, length: u16); 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_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_listen_with_backlog(pcb: *mut tcp_pcb, backlog: u8) -> *mut tcp_pcb;
pub fn tcp_accept(pcb: *mut tcp_pcb, pub fn tcp_accept(pcb: *mut tcp_pcb,
accept: extern fn(arg: *mut c_void, newpcb: *mut tcp_pcb, accept: Option<extern fn(arg: *mut c_void, newpcb: *mut tcp_pcb,
err: err) -> err); err: err) -> err>);
pub fn tcp_connect(pcb: *mut tcp_pcb, ipaddr: *mut ip_addr, port: u16, 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; 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_write(pcb: *mut tcp_pcb, dataptr: *const c_void, len: u16, apiflags: u8) -> err;
pub fn tcp_sent(pcb: *mut tcp_pcb, 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, pub fn tcp_recv(pcb: *mut tcp_pcb,
recv: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, p: *mut pbuf, recv: Option<extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, p: *mut pbuf,
err: err) -> err); err: err) -> err>);
pub fn tcp_recved(pcb: *mut tcp_pcb, len: u16); pub fn tcp_recved(pcb: *mut tcp_pcb, len: u16);
pub fn tcp_poll(pcb: *mut tcp_pcb, 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); interval: u8);
pub fn tcp_shutdown(pcb: *mut tcp_pcb, shut_rx: c_int, shut_tx: c_int) -> err; 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_close(pcb: *mut tcp_pcb) -> err;
pub fn tcp_abort(pcb: *mut tcp_pcb); pub fn tcp_abort(pcb: *mut tcp_pcb);
pub fn tcp_err(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 // nonstandard
pub fn tcp_sndbuf_(pcb: *mut tcp_pcb) -> u16; 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() -> *mut udp_pcb;
pub fn udp_new_ip_type(type_: ip_addr_type) -> *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_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_sendto(pcb: *mut udp_pcb, p: *mut pbuf, ipaddr: *mut ip_addr, port: u16) -> err;
pub fn udp_recv(pcb: *mut udp_pcb, pub fn udp_recv(pcb: *mut udp_pcb,
recv: extern fn(arg: *mut c_void, upcb: *mut udp_pcb, p: *mut pbuf, recv: Option<extern fn(arg: *mut c_void, upcb: *mut udp_pcb, p: *mut pbuf,
addr: *mut ip_addr, port: u16), addr: *mut ip_addr, port: u16)>,
recv_arg: *mut c_void); recv_arg: *mut c_void);
} }

View File

@ -9,4 +9,8 @@ path = "lib.rs"
[dependencies] [dependencies]
lwip-sys = { path = "../liblwip-sys" } lwip-sys = { path = "../liblwip-sys" }
std_artiq = { path = "../libstd_artiq" } std_artiq = { path = "../libstd_artiq", features = ["alloc"] }
[features]
default = ["preemption"]
preemption = []

View File

@ -8,7 +8,9 @@ extern crate lwip_sys;
extern crate std_artiq as std; extern crate std_artiq as std;
use core::marker::PhantomData; use core::marker::PhantomData;
use core::ptr;
use core::cell::RefCell; use core::cell::RefCell;
use core::fmt;
use alloc::boxed::Box; use alloc::boxed::Box;
use collections::LinkedList; use collections::LinkedList;
use libc::c_void; use libc::c_void;
@ -32,6 +34,8 @@ pub enum Error {
ConnectionReset, ConnectionReset,
ConnectionClosed, ConnectionClosed,
IllegalArgument, IllegalArgument,
// Not used by lwip; added for building blocking interfaces.
Interrupted
} }
impl Error { impl Error {
@ -53,6 +57,7 @@ impl Error {
Error::ConnectionReset => "connection reset", Error::ConnectionReset => "connection reset",
Error::ConnectionClosed => "connection closed", Error::ConnectionClosed => "connection closed",
Error::IllegalArgument => "illegal argument", Error::IllegalArgument => "illegal argument",
Error::Interrupted => "interrupted"
} }
} }
} }
@ -71,7 +76,12 @@ impl error::Error for Error {
impl From<Error> for std::io::Error { impl From<Error> for std::io::Error {
fn from(lower: Error) -> 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum IpAddr { pub enum IpAddr {
Ip4([u8; 4]), V4([u8; 4]),
Ip6([u16; 8]), V6([u16; 8]),
IpAny Any
} }
pub const IP4_ANY: IpAddr = IpAddr::Ip4([0, 0, 0, 0]); pub const IP4_ANY: IpAddr = IpAddr::V4([0, 0, 0, 0]);
pub const IP6_ANY: IpAddr = IpAddr::Ip6([0, 0, 0, 0, 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::IpAny; 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 { impl IpAddr {
fn into_raw(self) -> lwip_sys::ip_addr { fn into_raw(self) -> lwip_sys::ip_addr {
match self { match self {
IpAddr::Ip4(ref octets) => IpAddr::V4(octets) =>
lwip_sys::ip_addr { lwip_sys::ip_addr {
data: [(octets[0] as u32) << 24 | data: [(octets[0] as u32) << 24 |
(octets[1] as u32) << 16 | (octets[1] as u32) << 16 |
@ -123,7 +168,7 @@ impl IpAddr {
0, 0, 0], 0, 0, 0],
type_: lwip_sys::IPADDR_TYPE_V4 type_: lwip_sys::IPADDR_TYPE_V4
}, },
IpAddr::Ip6(ref segments) => IpAddr::V6(segments) =>
lwip_sys::ip_addr { lwip_sys::ip_addr {
data: [(segments[0] as u32) << 16 | (segments[1] as u32), data: [(segments[0] as u32) << 16 | (segments[1] as u32),
(segments[2] as u32) << 16 | (segments[3] 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)], (segments[6] as u32) << 16 | (segments[7] as u32)],
type_: lwip_sys::IPADDR_TYPE_V6 type_: lwip_sys::IPADDR_TYPE_V6
}, },
IpAddr::IpAny => IpAddr::Any =>
lwip_sys::ip_addr { lwip_sys::ip_addr {
data: [0; 4], data: [0; 4],
type_: lwip_sys::IPADDR_TYPE_ANY type_: lwip_sys::IPADDR_TYPE_ANY
@ -142,17 +187,17 @@ impl IpAddr {
unsafe fn from_raw(raw: *mut lwip_sys::ip_addr) -> IpAddr { unsafe fn from_raw(raw: *mut lwip_sys::ip_addr) -> IpAddr {
match *raw { match *raw {
lwip_sys::ip_addr { type_: lwip_sys::IPADDR_TYPE_V4, data } => 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] >> 16) as u8,
(data[0] >> 8) as u8, (data[0] >> 8) as u8,
(data[0] >> 0) as u8]), (data[0] >> 0) as u8]),
lwip_sys::ip_addr { type_: lwip_sys::IPADDR_TYPE_V6, data } => 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[1] >> 16) as u16, data[1] as u16,
(data[2] >> 16) as u16, data[2] as u16, (data[2] >> 16) as u16, data[2] as u16,
(data[3] >> 16) as u16, data[3] as u16]), (data[3] >> 16) as u16, data[3] as u16]),
lwip_sys::ip_addr { type_: lwip_sys::IPADDR_TYPE_ANY, .. } => lwip_sys::ip_addr { type_: lwip_sys::IPADDR_TYPE_ANY, .. } =>
IpAddr::IpAny IpAddr::Any
} }
} }
} }
@ -163,6 +208,12 @@ pub struct SocketAddr {
pub port: u16 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 { impl SocketAddr {
pub fn new(ip: IpAddr, port: u16) -> SocketAddr { pub fn new(ip: IpAddr, port: u16) -> SocketAddr {
SocketAddr { ip: ip, port: port } SocketAddr { ip: ip, port: port }
@ -175,6 +226,9 @@ pub struct Pbuf<'payload> {
phantom: PhantomData<&'payload [u8]> phantom: PhantomData<&'payload [u8]>
} }
#[cfg(not(feature = "preemption"))]
unsafe impl<'payload> Send for Pbuf<'payload> {}
impl<'payload> Pbuf<'payload> { impl<'payload> Pbuf<'payload> {
unsafe fn from_raw(raw: *mut lwip_sys::pbuf) -> Pbuf<'payload> { unsafe fn from_raw(raw: *mut lwip_sys::pbuf) -> Pbuf<'payload> {
Pbuf { raw: raw, phantom: PhantomData } Pbuf { raw: raw, phantom: PhantomData }
@ -259,6 +313,9 @@ pub struct UdpSocket {
state: Box<RefCell<UdpSocketState>> state: Box<RefCell<UdpSocketState>>
} }
#[cfg(not(feature = "preemption"))]
unsafe impl Send for UdpSocket {}
impl UdpSocket { impl UdpSocket {
pub fn new() -> Result<UdpSocket> { pub fn new() -> Result<UdpSocket> {
extern fn recv(arg: *mut c_void, _pcb: *mut lwip_sys::udp_pcb, extern fn recv(arg: *mut c_void, _pcb: *mut lwip_sys::udp_pcb,
@ -279,12 +336,12 @@ impl UdpSocket {
recv_buffer: LinkedList::new() recv_buffer: LinkedList::new()
})); }));
let arg = &mut *state as *mut RefCell<UdpSocketState> as *mut _; 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 }) Ok(UdpSocket { raw: raw, state: state })
} }
} }
pub fn state(&self) -> *const RefCell<UdpSocketState> { pub fn state(&self) -> &RefCell<UdpSocketState> {
&*self.state &*self.state
} }
@ -347,6 +404,9 @@ pub struct TcpListener {
state: Box<RefCell<TcpListenerState>> state: Box<RefCell<TcpListenerState>>
} }
#[cfg(not(feature = "preemption"))]
unsafe impl Send for TcpListener {}
impl TcpListener { impl TcpListener {
pub fn bind(addr: SocketAddr) -> Result<TcpListener> { pub fn bind(addr: SocketAddr) -> Result<TcpListener> {
extern fn accept(arg: *mut c_void, newpcb: *mut lwip_sys::tcp_pcb, 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 _; let arg = &mut *state as *mut RefCell<TcpListenerState> as *mut _;
lwip_sys::tcp_arg(raw2, arg); 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 }) Ok(TcpListener { raw: raw2, state: state })
} }
} }
pub fn state(&self) -> *const RefCell<TcpListenerState> { pub fn state(&self) -> &RefCell<TcpListenerState> {
&*self.state &*self.state
} }
pub fn try_accept(&self) -> Option<TcpStream> { pub fn try_accept(&self) -> Option<TcpStream> {
self.state.borrow_mut().backlog.pop_front() 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 { impl Drop for TcpListener {
@ -409,7 +481,8 @@ pub enum Shutdown {
#[derive(Debug)] #[derive(Debug)]
pub struct TcpStreamState { pub struct TcpStreamState {
recv_buffer: LinkedList<Result<Pbuf<'static>>>, recv_buffer: LinkedList<Result<Pbuf<'static>>>,
send_avail: usize send_avail: usize,
total_sent: usize
} }
impl TcpStreamState { impl TcpStreamState {
@ -428,6 +501,9 @@ pub struct TcpStream {
state: Box<RefCell<TcpStreamState>> state: Box<RefCell<TcpStreamState>>
} }
#[cfg(not(feature = "preemption"))]
unsafe impl Send for TcpStream {}
impl TcpStream { impl TcpStream {
fn from_raw(raw: *mut lwip_sys::tcp_pcb) -> TcpStream { fn from_raw(raw: *mut lwip_sys::tcp_pcb) -> TcpStream {
extern fn recv(arg: *mut c_void, _raw: *mut lwip_sys::tcp_pcb, 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, extern fn sent(arg: *mut c_void, raw: *mut lwip_sys::tcp_pcb,
_len: u16) -> lwip_sys::err { len: u16) -> lwip_sys::err {
unsafe { unsafe {
let state = arg as *mut RefCell<TcpStreamState>; 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 lwip_sys::ERR_OK
} }
@ -463,37 +541,56 @@ impl TcpStream {
unsafe { unsafe {
let mut state = Box::new(RefCell::new(TcpStreamState { let mut state = Box::new(RefCell::new(TcpStreamState {
recv_buffer: LinkedList::new(), 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 _; let arg = &mut *state as *mut RefCell<TcpStreamState> as *mut _;
lwip_sys::tcp_arg(raw, arg); lwip_sys::tcp_arg(raw, arg);
lwip_sys::tcp_recv(raw, recv); lwip_sys::tcp_recv(raw, Some(recv));
lwip_sys::tcp_sent(raw, sent); lwip_sys::tcp_sent(raw, Some(sent));
lwip_sys::tcp_err(raw, err); lwip_sys::tcp_err(raw, Some(err));
lwip_sys::tcp_nagle_disable_(raw);
TcpStream { raw: raw, state: state } TcpStream { raw: raw, state: state }
} }
} }
pub fn state(&self) -> *const RefCell<TcpStreamState> { pub fn state(&self) -> &RefCell<TcpStreamState> {
&*self.state &*self.state
} }
pub fn write(&self, data: &[u8]) -> Result<usize> { unsafe fn write_common(&self, data: &[u8], copy: bool) -> Result<usize> {
let sndbuf = unsafe { lwip_sys::tcp_sndbuf_(self.raw) } as usize; let sndbuf = lwip_sys::tcp_sndbuf_(self.raw) as usize;
let len = if data.len() < sndbuf { data.len() } else { sndbuf }; 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(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); }, || 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 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<()> { pub fn flush(&self) -> Result<()> {
const EMPTY_DATA: [u8; 0] = [];
result_from(unsafe { 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(_) => () Some(_) => ()
} }
match state.recv_buffer.pop_front() { 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!() _ => unreachable!()
} }
} }
@ -533,6 +633,11 @@ impl TcpStream {
impl Drop for TcpStream { impl Drop for TcpStream {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { 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 // tcp_close can fail here, but in drop() we don't care
let _ = lwip_sys::tcp_close(self.raw); let _ = lwip_sys::tcp_close(self.raw);
} }

View File

@ -9,3 +9,6 @@ path = "lib.rs"
[dependencies] [dependencies]
alloc_artiq = { path = "../liballoc_artiq" } alloc_artiq = { path = "../liballoc_artiq" }
[features]
alloc = []

View File

@ -17,6 +17,8 @@ use core::cmp;
use core::fmt; use core::fmt;
use io::{self, DEFAULT_BUF_SIZE, Error, ErrorKind, SeekFrom}; use io::{self, DEFAULT_BUF_SIZE, Error, ErrorKind, SeekFrom};
use io::memchr; use io::memchr;
use alloc::boxed::Box;
use collections::vec::Vec;
/// The `BufReader` struct adds buffering to any reader. /// The `BufReader` struct adds buffering to any reader.
/// ///

View File

@ -13,6 +13,8 @@ use io::prelude::*;
use core::cmp; use core::cmp;
use io::{self, SeekFrom, Error, ErrorKind}; use io::{self, SeekFrom, Error, ErrorKind};
use alloc::boxed::Box;
use collections::vec::Vec;
/// A `Cursor` wraps another type and provides it with a /// A `Cursor` wraps another type and provides it with a
/// [`Seek`](trait.Seek.html) implementation. /// [`Seek`](trait.Seek.html) implementation.

View File

@ -8,7 +8,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // 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::convert::Into;
use core::fmt; use core::fmt;
use core::marker::{Send, Sync}; use core::marker::{Send, Sync};
@ -62,13 +63,19 @@ pub struct Error {
enum Repr { enum Repr {
Os(i32), Os(i32),
#[cfg(feature="alloc")]
Custom(Box<Custom>), Custom(Box<Custom>),
#[cfg(not(feature="alloc"))]
Custom(Custom),
} }
#[derive(Debug)] #[derive(Debug)]
struct Custom { struct Custom {
kind: ErrorKind, kind: ErrorKind,
#[cfg(feature="alloc")]
error: Box<error::Error+Send+Sync>, error: Box<error::Error+Send+Sync>,
#[cfg(not(feature="alloc"))]
error: &'static str
} }
/// A list specifying general categories of I/O error. /// A list specifying general categories of I/O error.
@ -162,12 +169,21 @@ impl Error {
/// // errors can also be created from other errors /// // errors can also be created from other errors
/// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error);
/// ``` /// ```
#[cfg(feature="alloc")]
pub fn new<E>(kind: ErrorKind, error: E) -> Error pub fn new<E>(kind: ErrorKind, error: E) -> Error
where E: Into<Box<error::Error+Send+Sync>> where E: Into<Box<error::Error+Send+Sync>>
{ {
Self::_new(kind, error.into()) 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 { fn _new(kind: ErrorKind, error: Box<error::Error+Send+Sync>) -> Error {
Error { Error {
repr: Repr::Custom(Box::new(Custom { 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. /// Creates a new instance of an `Error` from a particular OS error code.
pub fn from_raw_os_error(code: i32) -> Error { pub fn from_raw_os_error(code: i32) -> Error {
Error { repr: Repr::Os(code) } Error { repr: Repr::Os(code) }
@ -198,6 +224,7 @@ impl Error {
/// ///
/// If this `Error` was constructed via `new` then this function will /// If this `Error` was constructed via `new` then this function will
/// return `Some`, otherwise it will return `None`. /// return `Some`, otherwise it will return `None`.
#[cfg(feature="alloc")]
pub fn get_ref(&self) -> Option<&(error::Error+Send+Sync+'static)> { pub fn get_ref(&self) -> Option<&(error::Error+Send+Sync+'static)> {
match self.repr { match self.repr {
Repr::Os(..) => None, Repr::Os(..) => None,
@ -210,6 +237,7 @@ impl Error {
/// ///
/// If this `Error` was constructed via `new` then this function will /// If this `Error` was constructed via `new` then this function will
/// return `Some`, otherwise it will return `None`. /// return `Some`, otherwise it will return `None`.
#[cfg(feature="alloc")]
pub fn get_mut(&mut self) -> Option<&mut (error::Error+Send+Sync+'static)> { pub fn get_mut(&mut self) -> Option<&mut (error::Error+Send+Sync+'static)> {
match self.repr { match self.repr {
Repr::Os(..) => None, Repr::Os(..) => None,
@ -221,6 +249,7 @@ impl Error {
/// ///
/// If this `Error` was constructed via `new` then this function will /// If this `Error` was constructed via `new` then this function will
/// return `Some`, otherwise it will return `None`. /// return `Some`, otherwise it will return `None`.
#[cfg(feature="alloc")]
pub fn into_inner(self) -> Option<Box<error::Error+Send+Sync>> { pub fn into_inner(self) -> Option<Box<error::Error+Send+Sync>> {
match self.repr { match self.repr {
Repr::Os(..) => None, Repr::Os(..) => None,
@ -282,14 +311,24 @@ impl error::Error for Error {
ErrorKind::UnexpectedEof => "unexpected end of file", ErrorKind::UnexpectedEof => "unexpected end of file",
ErrorKind::__Nonexhaustive => unreachable!() 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> { fn cause(&self) -> Option<&error::Error> {
match self.repr { match self.repr {
Repr::Os(..) => None, 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 }
}
} }
} }
} }

View File

@ -20,6 +20,3 @@
pub use super::{Read, Write, Seek}; pub use super::{Read, Write, Seek};
pub use super::BufRead; pub use super::BufRead;
pub use alloc::boxed::Box;
pub use collections::vec::Vec;

View File

@ -1,6 +1,7 @@
#![feature(lang_items, asm, alloc, collections, libc, needs_panic_runtime, #![feature(lang_items, asm, alloc, collections, libc, needs_panic_runtime,
question_mark, unicode, reflect_marker, raw, int_error_internals, 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] #![no_std]
#![needs_panic_runtime] #![needs_panic_runtime]
@ -8,6 +9,7 @@ extern crate rustc_unicode;
extern crate alloc_artiq; extern crate alloc_artiq;
extern crate alloc; extern crate alloc;
#[macro_use] #[macro_use]
#[macro_reexport(vec, format)]
extern crate collections; extern crate collections;
extern crate libc; 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 prelude {
pub mod v1 { pub mod v1 {
pub use core::prelude::v1::*; pub use core::prelude::v1::*;
pub use collections::*; pub use collections::boxed::Box;
pub use io::{Read, Write, Seek}; pub use collections::borrow::ToOwned;
pub use io::BufRead; pub use collections::string::{String, ToString};
pub use collections::vec::Vec;
} }
} }
pub mod time;
pub mod error; pub mod error;
pub mod io; pub mod io;
use core::fmt::Write; // Provide Box::new wrapper
#[cfg(not(feature="alloc"))]
#[macro_export] struct FakeBox<T>(core::marker::PhantomData<T>);
macro_rules! print { #[cfg(not(feature="alloc"))]
($($arg:tt)*) => ($crate::print_fmt(format_args!($($arg)*))); impl<T> FakeBox<T> {
} fn new(val: T) -> T {
val
#[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(())
} }
} }
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 {}
}

View File

@ -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)
}
}
}

View File

@ -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")
}
}
}

View File

@ -1,5 +0,0 @@
pub use self::duration::Duration;
pub use self::instant::Instant;
mod duration;
mod instant;

View 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)
}
}
}

View 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(())
}
}

View 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])
}
}

View 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;
}
}
}

View 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)
}
}

View 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() }
}

View 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
}

View 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::*;

View File

@ -1,47 +1,144 @@
#![no_std] #![no_std]
#![feature(libc, const_fn, try_borrow, stmt_expr_attributes, repr_simd, asm,
lang_items)]
#[macro_use] #[macro_use]
extern crate std_artiq as std; 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 { extern {
fn network_init(); fn network_init();
fn lwip_service(); fn lwip_service();
} }
fn timer(waiter: io::Waiter) { include!(concat!(env!("OUT_DIR"), "/git_info.rs"));
loop {
println!("tick");
waiter.sleep(std::time::Duration::from_millis(1000)).unwrap();
}
}
fn echo(waiter: io::Waiter) { // Allow linking with crates that are built as -Cpanic=unwind even if we use -Cpanic=abort.
let addr = io::SocketAddr::new(io::IP_ANY, 1234); // This is never called.
let listener = io::TcpListener::bind(waiter, addr).unwrap(); #[allow(non_snake_case)]
loop { #[no_mangle]
let (mut stream, _addr) = listener.accept().unwrap(); pub extern "C" fn _Unwind_Resume() -> ! {
loop { loop {}
let mut buf = [0];
stream.read(&mut buf).unwrap();
stream.write(&buf).unwrap();
}
}
} }
#[no_mangle] #[no_mangle]
pub unsafe extern fn rust_main() { pub unsafe extern fn rust_main() {
println!("Accepting network sessions in Rust."); 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]));
clock::init();
rtio_crg::init();
network_init(); network_init();
let mut scheduler = io::Scheduler::new(); let mut scheduler = sched::Scheduler::new();
scheduler.spawn(4096, timer); scheduler.spawner().spawn(16384, session::thread);
scheduler.spawn(4096, echo); #[cfg(has_rtio_moninj)]
scheduler.spawner().spawn(4096, moninj::thread);
#[cfg(has_rtio_analyzer)]
scheduler.spawner().spawn(4096, analyzer::thread);
loop { loop {
scheduler.run();
lwip_service(); lwip_service();
scheduler.run() }
})
}
#[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
}

View 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());
}
}
}
}

View 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) }
}

View 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)
}
}
}

View 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(())
}
}

View 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())
}

View 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(())
}
}
}

View 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)
})
}
}

View 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};

View File

@ -1,16 +1,17 @@
extern crate fringe; #![allow(dead_code)]
extern crate lwip;
use std::cell::RefCell; use std::cell::RefCell;
use std::vec::Vec; use std::vec::Vec;
use std::time::{Instant, Duration};
use std::io::{Read, Write, Result, Error, ErrorKind}; use std::io::{Read, Write, Result, Error, ErrorKind};
use self::fringe::OwnedStack; use fringe::OwnedStack;
use self::fringe::generator::{Generator, Yielder}; use fringe::generator::{Generator, Yielder, State as GeneratorState};
use lwip;
use clock;
use urc::Urc;
#[derive(Debug)] #[derive(Debug)]
struct WaitRequest { struct WaitRequest {
timeout: Option<Instant>, timeout: Option<u64>,
event: Option<WaitEvent> event: Option<WaitEvent>
} }
@ -28,43 +29,91 @@ struct Thread {
interrupted: bool interrupted: bool
} }
#[derive(Debug)] impl Thread {
pub struct Scheduler { unsafe fn new<F>(spawner: Spawner, stack_size: usize, f: F) -> ThreadHandle
threads: Vec<Thread>, where F: 'static + FnOnce(Waiter, Spawner) + Send {
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) {
let stack = OwnedStack::new(stack_size); let stack = OwnedStack::new(stack_size);
let thread = Thread { ThreadHandle::new(Thread {
generator: Generator::unsafe_new(stack, move |yielder, _| { generator: Generator::unsafe_new(stack, |yielder, _| {
f(Waiter(yielder)) f(Waiter(yielder), spawner)
}), }),
waiting_for: WaitRequest { waiting_for: WaitRequest {
timeout: None, timeout: None,
event: None event: None
}, },
interrupted: false 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) { pub fn run(&mut self) {
self.threads.append(&mut *self.spawner.queue.borrow_mut());
if self.threads.len() == 0 { return } if self.threads.len() == 0 { return }
let now = Instant::now(); let now = clock::get_ms();
let start_index = self.index; let start_index = self.index;
loop { loop {
self.index = (self.index + 1) % self.threads.len(); self.index = (self.index + 1) % self.threads.len();
let result = { let result = {
let thread = &mut self.threads[self.index]; let thread = &mut *self.threads[self.index].0.borrow_mut();
match thread.waiting_for { match thread.waiting_for {
_ if thread.interrupted => { _ if thread.interrupted => {
thread.interrupted = false; thread.interrupted = false;
@ -95,7 +144,8 @@ impl Scheduler {
}, },
Some(wait_request) => { Some(wait_request) => {
// The thread has suspended itself. // 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 { enum WaitEvent {
Completion(*const (Fn() -> bool + 'static)),
Termination(*const RefCell<Thread>),
UdpReadable(*const RefCell<lwip::UdpSocketState>), UdpReadable(*const RefCell<lwip::UdpSocketState>),
TcpAcceptable(*const RefCell<lwip::TcpListenerState>), TcpAcceptable(*const RefCell<lwip::TcpListenerState>),
TcpWriteable(*const RefCell<lwip::TcpStreamState>), TcpWriteable(*const RefCell<lwip::TcpStreamState>),
@ -115,6 +184,10 @@ enum WaitEvent {
impl WaitEvent { impl WaitEvent {
fn completed(&self) -> bool { fn completed(&self) -> bool {
match *self { match *self {
WaitEvent::Completion(f) =>
unsafe { (*f)() },
WaitEvent::Termination(thread) =>
unsafe { (*thread).borrow().terminated() },
WaitEvent::UdpReadable(state) => WaitEvent::UdpReadable(state) =>
unsafe { (*state).borrow().readable() }, unsafe { (*state).borrow().readable() },
WaitEvent::TcpAcceptable(state) => 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 {} unsafe impl Send for WaitEvent {}
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Waiter<'a>(&'a Yielder<WaitResult, WaitRequest, OwnedStack>); pub struct Waiter<'a>(&'a Yielder<WaitResult, WaitRequest, OwnedStack>);
impl<'a> Waiter<'a> { impl<'a> Waiter<'a> {
pub fn sleep(&self, duration: Duration) -> Result<()> { pub fn sleep(&self, duration_ms: u64) -> Result<()> {
let request = WaitRequest { let request = WaitRequest {
timeout: Some(Instant::now() + duration), timeout: Some(clock::get_ms() + duration_ms),
event: None 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<()> { pub fn udp_readable(&self, socket: &lwip::UdpSocket) -> Result<()> {
self.suspend(WaitRequest { self.suspend(WaitRequest {
timeout: None, timeout: None,
@ -185,7 +287,7 @@ impl<'a> Waiter<'a> {
// Wrappers around lwip // 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)] #[derive(Debug)]
pub struct UdpSocket<'a> { 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<()> { pub fn bind(&self, addr: SocketAddr) -> Result<()> {
Ok(try!(self.lower.bind(addr))) Ok(try!(self.lower.bind(addr)))
} }
@ -218,12 +328,12 @@ impl<'a> UdpSocket<'a> {
Ok(buf.len()) 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)); try!(self.waiter.udp_readable(&self.lower));
let (pbuf, addr) = self.lower.try_recv().unwrap(); let (pbuf, addr) = self.lower.try_recv().unwrap();
let len = ::std::cmp::min(buf.len(), pbuf.len()); buf.clear();
(&mut buf[..len]).copy_from_slice(&pbuf.as_slice()[..len]); buf.extend_from_slice(&pbuf.as_slice());
Ok((len, addr)) Ok(addr)
} }
pub fn send(&self, buf: &[u8]) -> Result<usize> { 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]); (&mut buf[..len]).copy_from_slice(&pbuf.as_slice()[..len]);
Ok(len) Ok(len)
} }
pub fn readable(&self) -> bool {
self.lower.state().borrow().readable()
}
} }
#[derive(Debug)] #[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)> { pub fn accept(&self) -> Result<(TcpStream, SocketAddr)> {
try!(self.waiter.tcp_acceptable(&self.lower)); try!(self.waiter.tcp_acceptable(&self.lower));
let stream_lower = self.lower.try_accept().unwrap(); let stream_lower = self.lower.try_accept().unwrap();
@ -265,9 +387,23 @@ impl<'a> TcpListener<'a> {
buffer: None buffer: None
}, addr)) }, addr))
} }
pub fn acceptable(&self) -> bool {
self.lower.state().borrow().acceptable()
} }
pub use self::lwip::Shutdown; pub fn keepalive(&self) -> bool {
self.lower.keepalive()
}
pub fn set_keepalive(&self, keepalive: bool) {
self.lower.set_keepalive(keepalive)
}
}
pub use lwip::Shutdown;
pub struct TcpStreamInner(lwip::TcpStream, Option<(lwip::Pbuf<'static>, usize)>);
#[derive(Debug)] #[derive(Debug)]
pub struct TcpStream<'a> { pub struct TcpStream<'a> {
@ -277,23 +413,43 @@ pub struct TcpStream<'a> {
} }
impl<'a> 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<()> { pub fn shutdown(&self, how: Shutdown) -> Result<()> {
Ok(try!(self.lower.shutdown(how))) 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> { impl<'a> Read for TcpStream<'a> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> { fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
if self.buffer.is_none() { if self.buffer.is_none() {
try!(self.waiter.tcp_readable(&self.lower)); try!(self.waiter.tcp_readable(&self.lower));
let pbuf = try!(self.lower.try_read()).unwrap(); match self.lower.try_read() {
self.buffer = Some((pbuf, 0)) 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 (pbuf, pos) = self.buffer.take().unwrap();
let slice = &pbuf.as_slice()[pos..]; let slice = &pbuf.as_slice()[pos..];
let len = ::std::cmp::min(buf.len(), slice.len()); 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() { if len < slice.len() {
self.buffer = Some((pbuf, pos + len)) self.buffer = Some((pbuf, pos + len))
} }
@ -304,7 +460,9 @@ impl<'a> Read for TcpStream<'a> {
impl<'a> Write for TcpStream<'a> { impl<'a> Write for TcpStream<'a> {
fn write(&mut self, buf: &[u8]) -> Result<usize> { fn write(&mut self, buf: &[u8]) -> Result<usize> {
try!(self.waiter.tcp_writeable(&self.lower)); 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<()> { fn flush(&mut self) -> Result<()> {

View 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();
}
}

View 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(())
}
}

View 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)
}
}

View File

@ -3,23 +3,75 @@ include $(MISOC_DIRECTORY)/software/common.mak
PYTHON ?= python3.5 PYTHON ?= python3.5
OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \ OBJECTS := flash_storage.o main.o
session.o log.o analyzer.o moninj.o net_server.o bridge_ctl.o \ OBJECTS_KSUPPORT := ksupport_glue.o artiq_personality.o rtio.o dds.o i2c.o ad9154.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
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$(MISOC_DIRECTORY)/software/include/dyld \
-I$(LIBDYLD_DIRECTORY)/include \ -I$(LIBDYLD_DIRECTORY)/include \
-I$(LIBUNWIND_DIRECTORY) \ -I$(LIBUNWIND_DIRECTORY) \
-I$(LIBUNWIND_DIRECTORY)/../unwinder/include \ -I$(LIBUNWIND_DIRECTORY)/../unwinder/include \
-I$(LIBLWIP_DIRECTORY)/../lwip/src/include \ -I$(LIBLWIP_DIRECTORY)/../lwip/src/include \
-I$(LIBLWIP_DIRECTORY) \ -I$(LIBLWIP_DIRECTORY)
-DNDEBUG 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 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 %.bin: %.elf
$(OBJCOPY) -O binary $< $@ $(OBJCOPY) -O binary $< $@
@chmod -x $@ @chmod -x $@
@ -27,56 +79,10 @@ all: runtime.bin runtime.fbi
%.fbi: %.bin %.fbi: %.bin
@echo " MSCIMG " $@ && $(PYTHON) -m misoc.tools.mkmscimg -f -o $@ $< @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: clean:
$(RM) $(OBJECTS) $(OBJECTS_KSUPPORT) $(RM) $(OBJECTS) $(OBJECTS_KSUPPORT)
$(RM) runtime.elf runtime.bin runtime.fbi .*~ *~ $(RM) runtime.elf runtime.bin runtime.fbi .*~ *~
$(RM) ksupport.elf ksupport.bin $(RM) ksupport.elf ksupport.bin
$(RM) -rf cargo
.PHONY: all clean .PHONY: all clean

View File

@ -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

View File

@ -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 */

View File

@ -233,7 +233,7 @@ struct artiq_raised_exception {
struct _Unwind_Exception unwind; struct _Unwind_Exception unwind;
struct artiq_exception artiq; struct artiq_exception artiq;
int handled; int handled;
struct artiq_backtrace_item backtrace[1024]; uintptr_t backtrace[1024];
size_t backtrace_size; size_t backtrace_size;
}; };
@ -303,8 +303,7 @@ static _Unwind_Reason_Code __artiq_uncaught_exception(
uintptr_t pcOffset = pc - funcStart; uintptr_t pcOffset = pc - funcStart;
EH_LOG("===> uncaught (pc=%p+%p)", (void*)funcStart, (void*)pcOffset); EH_LOG("===> uncaught (pc=%p+%p)", (void*)funcStart, (void*)pcOffset);
inflight->backtrace[inflight->backtrace_size].function = funcStart; inflight->backtrace[inflight->backtrace_size] = funcStart + pcOffset;
inflight->backtrace[inflight->backtrace_size].offset = pcOffset;
++inflight->backtrace_size; ++inflight->backtrace_size;
if(actions & _UA_END_OF_STACK) { if(actions & _UA_END_OF_STACK) {

View File

@ -17,11 +17,6 @@ struct artiq_exception {
int64_t param[3]; int64_t param[3];
}; };
struct artiq_backtrace_item {
intptr_t function;
intptr_t offset;
};
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -48,7 +43,7 @@ void __artiq_reraise(void)
/* Called by the runtime */ /* Called by the runtime */
void __artiq_terminate(struct artiq_exception *artiq_exn, void __artiq_terminate(struct artiq_exception *artiq_exn,
struct artiq_backtrace_item *backtrace, uintptr_t *backtrace,
size_t backtrace_size) size_t backtrace_size)
__attribute__((noreturn)); __attribute__((noreturn));

View File

@ -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;
}
}
}

View File

@ -1,6 +0,0 @@
#ifndef __BRIDGE_H
#define __BRIDGE_H
void bridge_main(void);
#endif /* __BRIDGE_H */

View File

@ -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);
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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 */

View File

@ -6,7 +6,6 @@
#include "artiq_personality.h" #include "artiq_personality.h"
#include "rtio.h" #include "rtio.h"
#include "log.h"
#include "dds.h" #include "dds.h"
#define DURATION_WRITE (5 << CONFIG_RTIO_FINE_TS_WIDTH) #define DURATION_WRITE (5 << CONFIG_RTIO_FINE_TS_WIDTH)

View File

@ -9,7 +9,6 @@
#include <generated/mem.h> #include <generated/mem.h>
#include <generated/csr.h> #include <generated/csr.h>
#include "log.h"
#include "flash_storage.h" #include "flash_storage.h"
#if (defined CSR_SPIFLASH_BASE && defined CONFIG_SPIFLASH_PAGE_SIZE) #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; return 0;
if(record->size < 6) { if(record->size < 6) {
core_log("flash_storage might be corrupted: record size is %u (<6) at address %08x\n", // core_log("flash_storage might be corrupted: record size is %u (<6) at address %08x\n",
record->size, record->raw_record); // record->size, record->raw_record);
if(fatal) if(fatal)
*fatal = 1; *fatal = 1;
return 0; return 0;
} }
if(is->seek > is->buf_len - sizeof(record->size) - 2) { /* 2 is the minimum key length */ 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 " // core_log("flash_storage might be corrupted: END_MARKER missing at the end of "
"the storage sector\n"); // "the storage sector\n");
if(fatal) if(fatal)
*fatal = 1; *fatal = 1;
return 0; return 0;
} }
if(record->size > is->buf_len - is->seek) { if(record->size > is->buf_len - is->seek) {
core_log("flash_storage might be corrupted: invalid record_size %d at address %08x\n", // core_log("flash_storage might be corrupted: invalid record_size %d at address %08x\n",
record->size, record->raw_record); // record->size, record->raw_record);
if(fatal) if(fatal)
*fatal = 1; *fatal = 1;
return 0; 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; record->key_len = strnlen(record->key, record->size - sizeof(record->size)) + 1;
if(record->key_len == 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", // core_log("flash_storage might be corrupted: invalid key length at address %08x\n",
record->raw_record); // record->raw_record);
if(fatal) if(fatal)
*fatal = 1; *fatal = 1;
return 0; 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. return 0; // Storage is definitely full.
fatal_error: fatal_error:
core_log("fatal error: flash storage might be corrupted\n"); // core_log("fatal error: flash storage might be corrupted\n");
return 0; return 0;
} }
@ -295,8 +294,8 @@ unsigned int fs_read(const char *key, void *buffer, unsigned int buf_len, unsign
} }
} }
if(fatal) // if(fatal)
core_log("fatal error: flash storage might be corrupted\n"); // core_log("fatal error: flash storage might be corrupted\n");
return read_length; return read_length;
} }

View File

@ -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();
}

View File

@ -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;
}
}
}

View File

@ -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 */

View File

@ -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);
}

View File

@ -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 */

View File

@ -1,20 +1,23 @@
INCLUDE generated/output_format.ld INCLUDE generated/output_format.ld
STARTUP(crt0-or1k.o)
ENTRY(_start) ENTRY(_start)
INCLUDE generated/regions.ld 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. * 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 { 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, /* Kernel stack is at the end of main RAM. */
* before the runtime stack. Leave 1M for runtime stack. PROVIDE(_fstack = ORIGIN(main_ram) + LENGTH(main_ram) - 4);
*/
PROVIDE(_fstack = 0x40000000 + LENGTH(main_ram) - 1024*1024 - 4);
/* Force ld to make the ELF header as loadable. */ /* Force ld to make the ELF header as loadable. */
PHDRS PHDRS
@ -32,6 +35,16 @@ SECTIONS
_etext = .; _etext = .;
} :text } :text
/* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */
.got : {
_GLOBAL_OFFSET_TABLE_ = .;
*(.got)
} :text
.got.plt : {
*(.got.plt)
} :text
.rodata : .rodata :
{ {
. = ALIGN(4); . = ALIGN(4);
@ -43,12 +56,12 @@ SECTIONS
.eh_frame : .eh_frame :
{ {
*(.eh_frame) KEEP(*(.eh_frame))
} :text } :text
.eh_frame_hdr : .eh_frame_hdr :
{ {
*(.eh_frame_hdr) KEEP(*(.eh_frame_hdr))
} :text :eh_frame } :text :eh_frame
.data : .data :
@ -73,7 +86,10 @@ SECTIONS
*(COMMON) *(COMMON)
. = ALIGN(4); . = ALIGN(4);
_ebss = .; _ebss = .;
. = ALIGN(8); }
_heapstart = .;
/DISCARD/ :
{
*(.debug*)
} }
} }

View 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;
}

View File

@ -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));
}

View File

@ -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