diff --git a/MANIFEST.in b/MANIFEST.in index 2bdea6226..3fb533874 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ graft artiq/runtime +graft artiq/runtime.rs graft artiq/examples include artiq/gui/logo*.svg include versioneer.py diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index cfe6b0e17..16bda10f0 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -13,6 +13,15 @@ Release notes * Dynamic __getattr__'s returning RPC target methods are not supported anymore. Controller driver classes must define all their methods intended for RPC as members. +* Datasets requested by experiments are by default archived into their HDF5 + output. If this behavior is undesirable, turn it off by passing + ``archive=False`` to ``get_dataset``. + + +2.0 +--- + +No further notes. 2.0rc2 diff --git a/artiq/browser/files.py b/artiq/browser/files.py index 1a45c7938..1dfbac28e 100644 --- a/artiq/browser/files.py +++ b/artiq/browser/files.py @@ -187,8 +187,16 @@ class FilesDock(QtWidgets.QDockWidget): except: logger.warning("unable to read metadata from %s", info.filePath(), exc_info=True) + rd = dict() + if "archive" in f: + rd = {k: (True, v.value) for k, v in f["archive"].items()} if "datasets" in f: - rd = {k: (True, v.value) for k, v in f["datasets"].items()} + for k, v in f["datasets"].items(): + if k in rd: + logger.warning("dataset '%s' is both in archive and " + "outputs", k) + rd[k] = (True, v.value) + if rd: self.datasets.init(rd) self.dataset_changed.emit(info.filePath()) diff --git a/artiq/compiler/embedding.py b/artiq/compiler/embedding.py index 7c6b7f617..4a5208c4a 100644 --- a/artiq/compiler/embedding.py +++ b/artiq/compiler/embedding.py @@ -994,8 +994,24 @@ class Stitcher: else: assert False + is_async = False + if hasattr(host_function, "artiq_embedded") and \ + "async" in host_function.artiq_embedded.flags: + is_async = True + + if not builtins.is_none(ret_type) and is_async: + note = diagnostic.Diagnostic("note", + "function called here", {}, + loc) + diag = diagnostic.Diagnostic("fatal", + "functions that return a value cannot be defined as async RPCs", {}, + self._function_loc(host_function.artiq_embedded.function), + notes=[note]) + self.engine.process(diag) + function_type = types.TRPC(ret_type, - service=self.embedding_map.store_object(host_function)) + service=self.embedding_map.store_object(host_function), + async=is_async) self.functions[function] = function_type return function_type @@ -1007,7 +1023,11 @@ class Stitcher: if function in self.functions: pass - elif not hasattr(host_function, "artiq_embedded"): + elif not hasattr(host_function, "artiq_embedded") or \ + (host_function.artiq_embedded.core_name is None and + host_function.artiq_embedded.portable is False and + host_function.artiq_embedded.syscall is None and + host_function.artiq_embedded.forbidden is False): self._quote_rpc(function, loc) elif host_function.artiq_embedded.function is not None: if host_function.__name__ == "": diff --git a/artiq/compiler/module.py b/artiq/compiler/module.py index 355e3072d..0e07a5b60 100644 --- a/artiq/compiler/module.py +++ b/artiq/compiler/module.py @@ -81,13 +81,6 @@ class Module: embedding_map=self.embedding_map) return llvm_ir_generator.process(self.artiq_ir, attribute_writeback=True) - def entry_point(self): - """Return the name of the function that is the entry point of this module.""" - if self.name != "": - return self.name + ".__modinit__" - else: - return "__modinit__" - def __repr__(self): printer = types.TypePrinter() globals = ["%s: %s" % (var, printer.name(self.globals[var])) for var in self.globals] diff --git a/artiq/compiler/prelude.py b/artiq/compiler/prelude.py index 8c46176f9..e19ef6706 100644 --- a/artiq/compiler/prelude.py +++ b/artiq/compiler/prelude.py @@ -31,6 +31,7 @@ def globals(): # ARTIQ decorators "kernel": builtins.fn_kernel(), "portable": builtins.fn_kernel(), + "rpc": builtins.fn_kernel(), # ARTIQ context managers "parallel": builtins.obj_parallel(), diff --git a/artiq/compiler/targets.py b/artiq/compiler/targets.py index dff28bb2e..aab813737 100644 --- a/artiq/compiler/targets.py +++ b/artiq/compiler/targets.py @@ -161,9 +161,9 @@ class Target: return llmachine.emit_object(llmodule) - def link(self, objects, init_fn): + def link(self, objects): """Link the relocatable objects into a shared library for this target.""" - with RunTool([self.triple + "-ld", "-shared", "--eh-frame-hdr", "-init", init_fn] + + with RunTool([self.triple + "-ld", "-shared", "--eh-frame-hdr"] + ["{{obj{}}}".format(index) for index in range(len(objects))] + ["-o", "{output}"], output=b"", @@ -177,8 +177,7 @@ class Target: return library def compile_and_link(self, modules): - return self.link([self.assemble(self.compile(module)) for module in modules], - init_fn=modules[0].entry_point()) + return self.link([self.assemble(self.compile(module)) for module in modules]) def strip(self, library): with RunTool([self.triple + "-strip", "--strip-debug", "{library}", "-o", "{output}"], diff --git a/artiq/compiler/testbench/perf_embedding.py b/artiq/compiler/testbench/perf_embedding.py index 81c0c02db..87d35ee04 100644 --- a/artiq/compiler/testbench/perf_embedding.py +++ b/artiq/compiler/testbench/perf_embedding.py @@ -47,7 +47,7 @@ def main(): target = OR1KTarget() llvm_ir = target.compile(module) elf_obj = target.assemble(llvm_ir) - elf_shlib = target.link([elf_obj], init_fn=module.entry_point()) + elf_shlib = target.link([elf_obj]) benchmark(lambda: embed(), "ARTIQ embedding") @@ -61,7 +61,7 @@ def main(): benchmark(lambda: target.assemble(llvm_ir), "LLVM machine code emission") - benchmark(lambda: target.link([elf_obj], init_fn=module.entry_point()), + benchmark(lambda: target.link([elf_obj]), "Linking") benchmark(lambda: target.strip(elf_shlib), diff --git a/artiq/compiler/transforms/artiq_ir_generator.py b/artiq/compiler/transforms/artiq_ir_generator.py index 0c753e984..4575c381c 100644 --- a/artiq/compiler/transforms/artiq_ir_generator.py +++ b/artiq/compiler/transforms/artiq_ir_generator.py @@ -452,30 +452,30 @@ class ARTIQIRGenerator(algorithm.Visitor): self.current_block = body self.visit(node.body) post_body = self.current_block - - if any(node.orelse): - else_tail = self.add_block("while.else") - self.current_block = else_tail - self.visit(node.orelse) - post_else_tail = self.current_block - - tail = self.add_block("while.tail") - self.current_block = tail - - if any(node.orelse): - if not post_else_tail.is_terminated(): - post_else_tail.append(ir.Branch(tail)) - else: - else_tail = tail - - post_head.append(ir.BranchIf(cond, body, else_tail)) - if not post_body.is_terminated(): - post_body.append(ir.Branch(head)) - break_block.append(ir.Branch(tail)) finally: self.break_target = old_break self.continue_target = old_continue + if any(node.orelse): + else_tail = self.add_block("while.else") + self.current_block = else_tail + self.visit(node.orelse) + post_else_tail = self.current_block + + tail = self.add_block("while.tail") + self.current_block = tail + + if any(node.orelse): + if not post_else_tail.is_terminated(): + post_else_tail.append(ir.Branch(tail)) + else: + else_tail = tail + + post_head.append(ir.BranchIf(cond, body, else_tail)) + if not post_body.is_terminated(): + post_body.append(ir.Branch(head)) + break_block.append(ir.Branch(tail)) + def iterable_len(self, value, typ=_size_type): if builtins.is_listish(value.type): if isinstance(value, ir.Constant): @@ -541,33 +541,33 @@ class ARTIQIRGenerator(algorithm.Visitor): self.current_assign = None self.visit(node.body) post_body = self.current_block - - if any(node.orelse): - else_tail = self.add_block("for.else") - self.current_block = else_tail - self.visit(node.orelse) - post_else_tail = self.current_block - - tail = self.add_block("for.tail") - self.current_block = tail - - if any(node.orelse): - if not post_else_tail.is_terminated(): - post_else_tail.append(ir.Branch(tail)) - else: - else_tail = tail - - if node.trip_count is not None: - head.append(ir.Loop(node.trip_count, phi, cond, body, else_tail)) - else: - head.append(ir.BranchIf(cond, body, else_tail)) - if not post_body.is_terminated(): - post_body.append(ir.Branch(continue_block)) - break_block.append(ir.Branch(tail)) finally: self.break_target = old_break self.continue_target = old_continue + if any(node.orelse): + else_tail = self.add_block("for.else") + self.current_block = else_tail + self.visit(node.orelse) + post_else_tail = self.current_block + + tail = self.add_block("for.tail") + self.current_block = tail + + if any(node.orelse): + if not post_else_tail.is_terminated(): + post_else_tail.append(ir.Branch(tail)) + else: + else_tail = tail + + if node.trip_count is not None: + head.append(ir.Loop(node.trip_count, phi, cond, body, else_tail)) + else: + head.append(ir.BranchIf(cond, body, else_tail)) + if not post_body.is_terminated(): + post_body.append(ir.Branch(continue_block)) + break_block.append(ir.Branch(tail)) + def visit_Break(self, node): self.append(ir.Branch(self.break_target)) @@ -821,7 +821,7 @@ class ARTIQIRGenerator(algorithm.Visitor): timeout = self.visit(context_expr_node.args[0]) timeout_ms = self.append(ir.Arith(ast.Mult(loc=None), timeout, ir.Constant(1000, builtins.TFloat()))) - timeout_ms_int = self.append(ir.Coerce(timeout_ms, builtins.TInt32())) + timeout_ms_int = self.append(ir.Coerce(timeout_ms, builtins.TInt64())) watchdog_id = self.append(ir.Builtin("watchdog_set", [timeout_ms_int], builtins.TInt32())) diff --git a/artiq/compiler/transforms/asttyped_rewriter.py b/artiq/compiler/transforms/asttyped_rewriter.py index 199ea2ef9..3c840673e 100644 --- a/artiq/compiler/transforms/asttyped_rewriter.py +++ b/artiq/compiler/transforms/asttyped_rewriter.py @@ -509,7 +509,7 @@ class ASTTypedRewriter(algorithm.Transformer): visit_DictComp = visit_unsupported visit_Ellipsis = visit_unsupported visit_GeneratorExp = visit_unsupported - visit_Set = visit_unsupported + # visit_Set = visit_unsupported visit_SetComp = visit_unsupported visit_Starred = visit_unsupported visit_Yield = visit_unsupported diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 826db2b4d..c755a43ee 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -19,6 +19,7 @@ lli32 = ll.IntType(32) lli64 = ll.IntType(64) lldouble = ll.DoubleType() llptr = ll.IntType(8).as_pointer() +llptrptr = ll.IntType(8).as_pointer().as_pointer() llmetadata = ll.MetaData() @@ -322,8 +323,6 @@ class LLVMIRGenerator: llty = ll.FunctionType(llvoid, []) elif name == "llvm.floor.f64": llty = ll.FunctionType(lldouble, [lldouble]) - elif name == "llvm.round.f64": - llty = ll.FunctionType(lldouble, [lldouble]) elif name == "llvm.pow.f64": llty = ll.FunctionType(lldouble, [lldouble, lldouble]) elif name == "llvm.powi.f64": @@ -348,15 +347,18 @@ class LLVMIRGenerator: llty = ll.FunctionType(lli32, [llptr]) elif name == "strcmp": llty = ll.FunctionType(lli32, [llptr, llptr]) + elif name == "lround": + llty = ll.FunctionType(lli32, [lldouble]) elif name == "send_rpc": - llty = ll.FunctionType(llvoid, [lli32, llptr], - var_arg=True) + llty = ll.FunctionType(llvoid, [lli32, llptr, llptrptr]) + elif name == "send_async_rpc": + llty = ll.FunctionType(llvoid, [lli32, llptr, llptrptr]) elif name == "recv_rpc": llty = ll.FunctionType(lli32, [llptr]) elif name == "now": llty = lli64 elif name == "watchdog_set": - llty = ll.FunctionType(lli32, [lli32]) + llty = ll.FunctionType(lli32, [lli64]) elif name == "watchdog_clear": llty = ll.FunctionType(llvoid, [lli32]) else: @@ -366,7 +368,8 @@ class LLVMIRGenerator: llglobal = ll.Function(self.llmodule, llty, name) if name in ("__artiq_raise", "__artiq_reraise", "llvm.trap"): llglobal.attributes.add("noreturn") - if name in ("rtio_log", "send_rpc", "watchdog_set", "watchdog_clear", + if name in ("rtio_log", "send_rpc", "send_async_rpc", + "watchdog_set", "watchdog_clear", self.target.print_function): llglobal.attributes.add("nounwind") else: @@ -1041,9 +1044,8 @@ class LLVMIRGenerator: name=insn.name) elif insn.op == "round": llarg = self.map(insn.operands[0]) - llvalue = self.llbuilder.call(self.llbuiltin("llvm.round.f64"), [llarg]) - return self.llbuilder.fptosi(llvalue, self.llty_of_type(insn.type), - name=insn.name) + return self.llbuilder.call(self.llbuiltin("lround"), [llarg], + name=insn.name) elif insn.op == "globalenv": def get_outer(llenv, env_ty): if "$outer" in env_ty.params: @@ -1233,7 +1235,8 @@ class LLVMIRGenerator: llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [], name="rpc.stack") - llargs = [] + llargs = self.llbuilder.alloca(llptr, ll.Constant(lli32, len(args)), + name="rpc.args") for index, arg in enumerate(args): if builtins.is_none(arg.type): llargslot = self.llbuilder.alloca(ll.LiteralStructType([]), @@ -1243,14 +1246,24 @@ class LLVMIRGenerator: llargslot = self.llbuilder.alloca(llarg.type, name="rpc.arg{}".format(index)) self.llbuilder.store(llarg, llargslot) - llargs.append(llargslot) + llargslot = self.llbuilder.bitcast(llargslot, llptr) - self.llbuilder.call(self.llbuiltin("send_rpc"), - [llservice, lltag] + llargs) + llargptr = self.llbuilder.gep(llargs, [ll.Constant(lli32, index)]) + self.llbuilder.store(llargslot, llargptr) + + if fun_type.async: + self.llbuilder.call(self.llbuiltin("send_async_rpc"), + [llservice, lltag, llargs]) + else: + self.llbuilder.call(self.llbuiltin("send_rpc"), + [llservice, lltag, llargs]) # Don't waste stack space on saved arguments. self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr]) + if fun_type.async: + return ll.Undefined + # T result = { # void *ptr = NULL; # loop: int size = rpc_recv("tag", ptr); @@ -1286,6 +1299,7 @@ class LLVMIRGenerator: self.llbuilder.position_at_end(llalloc) llalloca = self.llbuilder.alloca(lli8, llsize, name="rpc.alloc") + llalloca.align = 4 # maximum alignment required by OR1K ABI llphi.add_incoming(llalloca, llalloc) self.llbuilder.branch(llhead) diff --git a/artiq/compiler/types.py b/artiq/compiler/types.py index e1781116e..05640b2ae 100644 --- a/artiq/compiler/types.py +++ b/artiq/compiler/types.py @@ -308,20 +308,22 @@ class TRPC(Type): :ivar ret: (:class:`Type`) return type :ivar service: (int) RPC service number + :ivar async: (bool) whether the RPC blocks until return """ attributes = OrderedDict() - def __init__(self, ret, service): + def __init__(self, ret, service, async=False): assert isinstance(ret, Type) - self.ret, self.service = ret, service + self.ret, self.service, self.async = ret, service, async def find(self): return self def unify(self, other): if isinstance(other, TRPC) and \ - self.service == other.service: + self.service == other.service and \ + self.async == other.async: self.ret.unify(other.ret) elif isinstance(other, TVar): other.unify(self) @@ -337,7 +339,8 @@ class TRPC(Type): def __eq__(self, other): return isinstance(other, TRPC) and \ - self.service == other.service + self.service == other.service and \ + self.async == other.async def __ne__(self, other): return not (self == other) @@ -727,7 +730,9 @@ class TypePrinter(object): elif isinstance(typ, TFunction): return signature elif isinstance(typ, TRPC): - return "[rpc #{}](...)->{}".format(typ.service, self.name(typ.ret, depth + 1)) + return "[rpc{} #{}](...)->{}".format(typ.service, + " async" if typ.async else "", + self.name(typ.ret, depth + 1)) elif isinstance(typ, TBuiltinFunction): return "".format(typ.name) elif isinstance(typ, (TConstructor, TExceptionConstructor)): diff --git a/artiq/coredevice/comm_generic.py b/artiq/coredevice/comm_generic.py index 8a8277ac1..8afa8c096 100644 --- a/artiq/coredevice/comm_generic.py +++ b/artiq/coredevice/comm_generic.py @@ -20,7 +20,7 @@ class _H2DMsgType(Enum): IDENT_REQUEST = 3 SWITCH_CLOCK = 4 - LOAD_LIBRARY = 5 + LOAD_KERNEL = 5 RUN_KERNEL = 6 RPC_REPLY = 7 @@ -68,9 +68,7 @@ RPCKeyword = namedtuple('RPCKeyword', ['name', 'value']) class CommGeneric: def __init__(self): - self._read_type = self._write_type = None - self._read_length = 0 - self._write_buffer = [] + self._read_type = None def open(self): """Opens the communication channel. @@ -99,10 +97,6 @@ class CommGeneric: def _read_header(self): self.open() - if self._read_length > 0: - raise IOError("Read underrun ({} bytes remaining)". - format(self._read_length)) - # Wait for a synchronization sequence, 5a 5a 5a 5a. sync_count = 0 while sync_count < 4: @@ -113,20 +107,11 @@ class CommGeneric: sync_count = 0 # Read message header. - (self._read_length, ) = struct.unpack(">l", self.read(4)) - if not self._read_length: # inband connection close - raise OSError("Connection closed") - (raw_type, ) = struct.unpack("B", self.read(1)) self._read_type = _D2HMsgType(raw_type) - if self._read_length < 9: - raise IOError("Read overrun in message header ({} remaining)". - format(self._read_length)) - self._read_length -= 9 - - logger.debug("receiving message: type=%r length=%d", - self._read_type, self._read_length) + logger.debug("receiving message: type=%r", + self._read_type) def _read_expect(self, ty): if self._read_type != ty: @@ -138,12 +123,6 @@ class CommGeneric: self._read_expect(ty) def _read_chunk(self, length): - if self._read_length < length: - raise IOError("Read overrun while trying to read {} bytes ({} remaining)" - " in packet {}". - format(length, self._read_length, self._read_type)) - - self._read_length -= length return self.read(length) def _read_int8(self): @@ -162,11 +141,14 @@ class CommGeneric: (value, ) = struct.unpack(">d", self._read_chunk(8)) return value + def _read_bool(self): + return True if self._read_int8() else False + def _read_bytes(self): return self._read_chunk(self._read_int32()) def _read_string(self): - return self._read_bytes()[:-1].decode("utf-8") + return self._read_bytes().decode("utf-8") # # Writer interface @@ -175,46 +157,38 @@ class CommGeneric: def _write_header(self, ty): self.open() - logger.debug("preparing to send message: type=%r", ty) - self._write_type = ty - self._write_buffer = [] + logger.debug("sending message: type=%r", ty) - def _write_flush(self): - # Calculate message size. - length = sum([len(chunk) for chunk in self._write_buffer]) - logger.debug("sending message: type=%r length=%d", self._write_type, length) - - # Write synchronization sequence, header and body. - self.write(struct.pack(">llB", 0x5a5a5a5a, - 9 + length, self._write_type.value)) - for chunk in self._write_buffer: - self.write(chunk) + # Write synchronization sequence and header. + self.write(struct.pack(">lB", 0x5a5a5a5a, ty.value)) def _write_empty(self, ty): self._write_header(ty) - self._write_flush() def _write_chunk(self, chunk): - self._write_buffer.append(chunk) + self.write(chunk) def _write_int8(self, value): - self._write_buffer.append(struct.pack("B", value)) + self.write(struct.pack("B", value)) def _write_int32(self, value): - self._write_buffer.append(struct.pack(">l", value)) + self.write(struct.pack(">l", value)) def _write_int64(self, value): - self._write_buffer.append(struct.pack(">q", value)) + self.write(struct.pack(">q", value)) def _write_float64(self, value): - self._write_buffer.append(struct.pack(">d", value)) + self.write(struct.pack(">d", value)) + + def _write_bool(self, value): + self.write(struct.pack("B", value)) def _write_bytes(self, value): self._write_int32(len(value)) - self._write_buffer.append(value) + self.write(value) def _write_string(self, value): - self._write_bytes(value.encode("utf-8") + b"\0") + self._write_bytes(value.encode("utf-8")) # # Exported APIs @@ -232,7 +206,7 @@ class CommGeneric: if runtime_id != b"AROR": raise UnsupportedDevice("Unsupported runtime ID: {}" .format(runtime_id)) - gateware_version = self._read_chunk(self._read_length).decode("utf-8") + gateware_version = self._read_string() if gateware_version != software_version and \ gateware_version + ".dirty" != software_version: logger.warning("Mismatch between gateware (%s) " @@ -242,7 +216,6 @@ class CommGeneric: def switch_clock(self, external): self._write_header(_H2DMsgType.SWITCH_CLOCK) self._write_int8(external) - self._write_flush() self._read_empty(_D2HMsgType.CLOCK_SWITCH_COMPLETED) @@ -251,7 +224,7 @@ class CommGeneric: self._read_header() self._read_expect(_D2HMsgType.LOG_REPLY) - return self._read_chunk(self._read_length).decode("utf-8", "replace") + return self._read_string() def clear_log(self): self._write_empty(_H2DMsgType.LOG_CLEAR) @@ -261,17 +234,15 @@ class CommGeneric: def flash_storage_read(self, key): self._write_header(_H2DMsgType.FLASH_READ_REQUEST) self._write_string(key) - self._write_flush() self._read_header() self._read_expect(_D2HMsgType.FLASH_READ_REPLY) - return self._read_chunk(self._read_length) + return self._read_string() def flash_storage_write(self, key, value): self._write_header(_H2DMsgType.FLASH_WRITE_REQUEST) self._write_string(key) self._write_bytes(value) - self._write_flush() self._read_header() if self._read_type == _D2HMsgType.FLASH_ERROR_REPLY: @@ -287,14 +258,12 @@ class CommGeneric: def flash_storage_remove(self, key): self._write_header(_H2DMsgType.FLASH_REMOVE_REQUEST) self._write_string(key) - self._write_flush() self._read_empty(_D2HMsgType.FLASH_OK_REPLY) def load(self, kernel_library): - self._write_header(_H2DMsgType.LOAD_LIBRARY) - self._write_chunk(kernel_library) - self._write_flush() + self._write_header(_H2DMsgType.LOAD_KERNEL) + self._write_bytes(kernel_library) self._read_empty(_D2HMsgType.LOAD_COMPLETED) @@ -442,25 +411,29 @@ class CommGeneric: raise IOError("Unknown RPC value tag: {}".format(repr(tag))) def _serve_rpc(self, embedding_map): - service_id = self._read_int32() - if service_id == 0: - service = lambda obj, attr, value: setattr(obj, attr, value) - else: - service = embedding_map.retrieve_object(service_id) - + async = self._read_bool() + service_id = self._read_int32() args, kwargs = self._receive_rpc_args(embedding_map) return_tags = self._read_bytes() - logger.debug("rpc service: [%d]%r %r %r -> %s", service_id, service, args, kwargs, return_tags) + + if service_id is 0: + service = lambda obj, attr, value: setattr(obj, attr, value) + else: + service = embedding_map.retrieve_object(service_id) + logger.debug("rpc service: [%d]%r%s %r %r -> %s", service_id, service, + (" (async)" if async else ""), args, kwargs, return_tags) + + if async: + service(*args, **kwargs) + return try: result = service(*args, **kwargs) - logger.debug("rpc service: %d %r %r == %r", service_id, args, kwargs, result) + logger.debug("rpc service: %d %r %r = %r", service_id, args, kwargs, result) - if service_id != 0: - self._write_header(_H2DMsgType.RPC_REPLY) - self._write_bytes(return_tags) - self._send_rpc_value(bytearray(return_tags), result, result, service) - self._write_flush() + self._write_header(_H2DMsgType.RPC_REPLY) + self._write_bytes(return_tags) + self._send_rpc_value(bytearray(return_tags), result, result, service) except Exception as exn: logger.debug("rpc service: %d %r %r ! %r", service_id, args, kwargs, exn) @@ -503,8 +476,6 @@ class CommGeneric: self._write_int32(-1) # column not known self._write_string(function) - self._write_flush() - def _serve_exception(self, embedding_map, symbolizer, demangler): name = self._read_string() message = self._read_string() diff --git a/artiq/coredevice/comm_tcp.py b/artiq/coredevice/comm_tcp.py index 7c9395609..9d0a22703 100644 --- a/artiq/coredevice/comm_tcp.py +++ b/artiq/coredevice/comm_tcp.py @@ -29,7 +29,6 @@ def initialize_connection(host, port): sock.settimeout(None) set_keepalive(sock, 3, 2, 3) logger.debug("connected to host %s on port %d", host, port) - sock.sendall(b"ARTIQ coredev\n") return sock @@ -44,6 +43,7 @@ class Comm(CommGeneric): if hasattr(self, "socket"): return self.socket = initialize_connection(self.host, self.port) + self.socket.sendall(b"ARTIQ coredev\n") def close(self): if not hasattr(self, "socket"): diff --git a/artiq/coredevice/core.py b/artiq/coredevice/core.py index 8f9330287..1b1b7b3a4 100644 --- a/artiq/coredevice/core.py +++ b/artiq/coredevice/core.py @@ -103,6 +103,7 @@ class Core: def run(self, function, args, kwargs): result = None + @rpc(flags={"async"}) def set_result(new_result): nonlocal result result = new_result diff --git a/artiq/dashboard/datasets.py b/artiq/dashboard/datasets.py index cdb8d3ef6..5e353d4c9 100644 --- a/artiq/dashboard/datasets.py +++ b/artiq/dashboard/datasets.py @@ -7,6 +7,7 @@ from PyQt5 import QtCore, QtWidgets from artiq.tools import short_format from artiq.gui.tools import LayoutWidget, QRecursiveFilterProxyModel from artiq.gui.models import DictSyncTreeSepModel +from artiq.gui.scientific_spinbox import ScientificSpinBox logger = logging.getLogger(__name__) @@ -50,10 +51,10 @@ class Editor(QtWidgets.QDialog): class NumberEditor(Editor): def get_edit_widget(self, initial_value): - self.edit_widget = QtWidgets.QDoubleSpinBox() - self.edit_widget.setMinimum(float("-inf")) - self.edit_widget.setMaximum(float("+inf")) - self.edit_widget.setDecimals(8) + self.edit_widget = ScientificSpinBox() + self.edit_widget.setDecimals(13) + self.edit_widget.setPrecision() + self.edit_widget.setRelativeStep() self.edit_widget.setValue(float(initial_value)) return self.edit_widget diff --git a/artiq/dashboard/moninj.py b/artiq/dashboard/moninj.py index b821865b0..22c0bb963 100644 --- a/artiq/dashboard/moninj.py +++ b/artiq/dashboard/moninj.py @@ -23,35 +23,26 @@ _mode_enc = { } -class _MoninjWidget(QtWidgets.QFrame): - def __init__(self): - QtWidgets.QFrame.__init__(self) - qfm = QtGui.QFontMetrics(self.font()) - self._size = QtCore.QSize( - 18*qfm.averageCharWidth(), - 6*qfm.lineSpacing()) - self.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) - - self.setFrameShape(QtWidgets.QFrame.Box) - self.setFrameShadow(QtWidgets.QFrame.Raised) - - def sizeHint(self): - return self._size - - -class _TTLWidget(_MoninjWidget): +class _TTLWidget(QtWidgets.QFrame): def __init__(self, channel, send_to_device, force_out, title): + QtWidgets.QFrame.__init__(self) + self.channel = channel self.send_to_device = send_to_device self.force_out = force_out - _MoninjWidget.__init__(self) + self.setFrameShape(QtWidgets.QFrame.Box) + self.setFrameShadow(QtWidgets.QFrame.Raised) grid = QtWidgets.QGridLayout() + grid.setContentsMargins(0, 0, 0, 0) + grid.setHorizontalSpacing(0) + grid.setVerticalSpacing(0) self.setLayout(grid) label = QtWidgets.QLabel(title) label.setAlignment(QtCore.Qt.AlignCenter) - label.setWordWrap(True) + label.setSizePolicy(QtWidgets.QSizePolicy.Ignored, + QtWidgets.QSizePolicy.Preferred) grid.addWidget(label, 1, 1) self.stack = QtWidgets.QStackedWidget() @@ -62,9 +53,18 @@ class _TTLWidget(_MoninjWidget): self.stack.addWidget(self.direction) grid_cb = LayoutWidget() - self.override = QtWidgets.QCheckBox("Override") + grid_cb.layout.setContentsMargins(0, 0, 0, 0) + grid_cb.layout.setHorizontalSpacing(0) + grid_cb.layout.setVerticalSpacing(0) + self.override = QtWidgets.QToolButton() + self.override.setText("OVR") + self.override.setCheckable(True) + self.override.setToolTip("Override") grid_cb.addWidget(self.override, 3, 1) - self.level = QtWidgets.QCheckBox("Level") + self.level = QtWidgets.QToolButton() + self.level.setText("LVL") + self.level.setCheckable(True) + self.level.setToolTip("Level") grid_cb.addWidget(self.level, 3, 2) self.stack.addWidget(grid_cb) @@ -78,19 +78,19 @@ class _TTLWidget(_MoninjWidget): grid.setRowStretch(4, 1) self.programmatic_change = False - self.override.stateChanged.connect(self.override_toggled) - self.level.stateChanged.connect(self.level_toggled) + self.override.clicked.connect(self.override_toggled) + self.level.clicked.connect(self.level_toggled) self.set_value(0, False, False) def enterEvent(self, event): self.stack.setCurrentIndex(1) - _MoninjWidget.enterEvent(self, event) + QtWidgets.QFrame.enterEvent(self, event) def leaveEvent(self, event): if not self.override.isChecked(): self.stack.setCurrentIndex(0) - _MoninjWidget.leaveEvent(self, event) + QtWidgets.QFrame.leaveEvent(self, event) def override_toggled(self, override): if self.programmatic_change: @@ -125,11 +125,11 @@ class _TTLWidget(_MoninjWidget): color = " color=\"red\"" else: color = "" - self.value.setText("{}".format( + self.value.setText("{}".format( color, value_s)) oe = oe or self.force_out direction = "OUT" if oe else "IN" - self.direction.setText("" + direction + "") + self.direction.setText("" + direction + "") self.programmatic_change = True try: @@ -144,24 +144,30 @@ class _TTLWidget(_MoninjWidget): return self.channel -class _DDSWidget(_MoninjWidget): +class _DDSWidget(QtWidgets.QFrame): def __init__(self, bus_channel, channel, sysclk, title): + QtWidgets.QFrame.__init__(self) + self.bus_channel = bus_channel self.channel = channel self.sysclk = sysclk - _MoninjWidget.__init__(self) + self.setFrameShape(QtWidgets.QFrame.Box) + self.setFrameShadow(QtWidgets.QFrame.Raised) grid = QtWidgets.QGridLayout() + grid.setContentsMargins(0, 0, 0, 0) + grid.setHorizontalSpacing(0) + grid.setVerticalSpacing(0) self.setLayout(grid) label = QtWidgets.QLabel(title) label.setAlignment(QtCore.Qt.AlignCenter) - label.setWordWrap(True) + label.setSizePolicy(QtWidgets.QSizePolicy.Ignored, + QtWidgets.QSizePolicy.Preferred) grid.addWidget(label, 1, 1) self.value = QtWidgets.QLabel() self.value.setAlignment(QtCore.Qt.AlignCenter) - self.value.setWordWrap(True) grid.addWidget(self.value, 2, 1, 6, 1) grid.setRowStretch(1, 1) @@ -172,7 +178,7 @@ class _DDSWidget(_MoninjWidget): def set_value(self, ftw): frequency = ftw*self.sysclk()/2**32 - self.value.setText("{:.7f} MHz" + self.value.setText("{:.7f} MHz" .format(frequency/1e6)) def sort_key(self): diff --git a/artiq/devices/pdq2/driver.py b/artiq/devices/pdq2/driver.py index ea16a205b..f15928c84 100644 --- a/artiq/devices/pdq2/driver.py +++ b/artiq/devices/pdq2/driver.py @@ -13,6 +13,17 @@ logger = logging.getLogger(__name__) class Segment: + """Serialize the lines for a single Segment. + + Attributes: + max_time (int): Maximum duration of a line. + max_val (int): Maximum absolute value (scale) of the DAC output. + max_out (float): Output voltage at :attr:`max_val`. In Volt. + out_scale (float): Steps per Volt. + cordic_gain (float): CORDIC amplitude gain. + addr (int): Address assigned to this segment. + data (bytes): Serialized segment data. + """ max_time = 1 << 16 # uint16 timer max_val = 1 << 15 # int16 DAC max_out = 10. # Volt @@ -27,6 +38,24 @@ class Segment: def line(self, typ, duration, data, trigger=False, silence=False, aux=False, shift=0, jump=False, clear=False, wait=False): + """Append a line to this segment. + + Args: + typ (int): Output module to target with this line. + duration (int): Duration of the line in units of + ``clock_period*2**shift``. + data (bytes): Opaque data for the output module. + trigger (bool): Wait for trigger assertion before executing + this line. + silence (bool): Disable DAC clocks for the duration of this line. + aux (bool): Assert the AUX (F5 TTL) output during this line. + shift (int): Duration and spline evolution exponent. + jump (bool): Return to the frame address table after this line. + clear (bool): Clear the DDS phase accumulator when starting to + exectute this line. + wait (bool): Wait for trigger assertion before executing the next + line. + """ assert len(data) % 2 == 0, data assert len(data)//2 <= 14 # assert dt*(1 << shift) > 1 + len(data)//2 @@ -39,6 +68,15 @@ class Segment: @staticmethod def pack(widths, values): + """Pack spline data. + + Args: + widths (list[int]): Widths of values in multiples of 16 bits. + values (list[int]): Values to pack. + + Returns: + data (bytes): Packed data. + """ fmt = "<" ud = [] for width, value in zip(widths, values): @@ -60,7 +98,11 @@ class Segment: def bias(self, amplitude=[], **kwargs): """Append a bias line to this segment. - Amplitude in volts + Args: + amplitude (list[float]): Amplitude coefficients in in Volts and + increasing powers of ``1/(2**shift*clock_period)``. + Discrete time compensation will be applied. + **kwargs: Passed to :meth:`line`. """ coef = [self.out_scale*a for a in amplitude] discrete_compensate(coef) @@ -68,12 +110,18 @@ class Segment: self.line(typ=0, data=data, **kwargs) def dds(self, amplitude=[], phase=[], **kwargs): - """Append a dds line to this segment. + """Append a DDS line to this segment. - Amplitude in volts, - phase[0] in turns, - phase[1] in turns*sample_rate, - phase[2] in turns*(sample_rate/2**shift)**2 + Args: + amplitude (list[float]): Amplitude coefficients in in Volts and + increasing powers of ``1/(2**shift*clock_period)``. + Discrete time compensation and CORDIC gain compensation + will be applied by this method. + phase (list[float]): Phase/frequency/chirp coefficients. + ``phase[0]`` in ``turns``, + ``phase[1]`` in ``turns/clock_period``, + ``phase[2]`` in ``turns/(clock_period**2*2**shift)``. + **kwargs: Passed to :meth:`line`. """ scale = self.out_scale/self.cordic_gain coef = [scale*a for a in amplitude] @@ -86,6 +134,13 @@ class Segment: class Channel: + """PDQ2 Channel. + + Attributes: + num_frames (int): Number of frames supported. + max_data (int): Number of 16 bit data words per channel. + segments (list[Segment]): Segments added to this channel. + """ num_frames = 8 max_data = 4*(1 << 10) # 8kx16 8kx16 4kx16 @@ -93,14 +148,27 @@ class Channel: self.segments = [] def clear(self): + """Remove all segments.""" self.segments.clear() def new_segment(self): + """Create and attach a new :class:`Segment` to this channel. + + Returns: + :class:`Segment` + """ segment = Segment() self.segments.append(segment) return segment def place(self): + """Place segments contiguously. + + Assign segment start addresses and determine length of data. + + Returns: + addr (int): Amount of memory in use on this channel. + """ addr = self.num_frames for segment in self.segments: segment.addr = addr @@ -109,6 +177,23 @@ class Channel: return addr def table(self, entry=None): + """Generate the frame address table. + + Unused frame indices are assigned the zero address in the frame address + table. + This will cause the memory parser to remain in the frame address table + until another frame is selected. + + The frame entry segments can be any segments in the channel. + + Args: + entry (list[Segment]): List of initial segments for each frame. + If not specified, the first :attr:`num_frames` segments are + used as frame entry points. + + Returns: + table (bytes): Frame address table. + """ table = [0] * self.num_frames if entry is None: entry = self.segments @@ -118,6 +203,18 @@ class Channel: return struct.pack("<" + "H"*self.num_frames, *table) def serialize(self, entry=None): + """Serialize the memory for this channel. + + Places the segments contiguously in memory after the frame table. + Allocates and assigns segment and frame table addresses. + Serializes segment data and prepends frame address table. + + Args: + entry (list[Segment]): See :meth:`table`. + + Returns: + data (bytes): Channel memory data. + """ self.place() data = b"".join([segment.data for segment in self.segments]) return self.table(entry) + data @@ -125,7 +222,22 @@ class Channel: class Pdq2: """ - PDQ DAC (a.k.a. QC_Waveform) + PDQ stack. + + Args: + url (str): Pyserial device URL. Can be ``hwgrep://`` style + (search for serial number, bus topology, USB VID:PID combination), + ``COM15`` for a Windows COM port number, + ``/dev/ttyUSB0`` for a Linux serial port. + dev (file-like): File handle to use as device. If passed, ``url`` is + ignored. + num_boards (int): Number of boards in this stack. + + Attributes: + num_dacs (int): Number of DAC outputs per board. + num_channels (int): Number of channels in this stack. + num_boards (int): Number of boards in this stack. + channels (list[Channel]): List of :class:`Channel` in this stack. """ num_dacs = 3 freq = 50e6 @@ -154,42 +266,58 @@ class Pdq2: self.freq = float(freq) def close(self): + """Close the USB device handle.""" self.dev.close() del self.dev def write(self, data): + """Write data to the PDQ2 board. + + Args: + data (bytes): Data to write. + """ logger.debug("> %r", data) written = self.dev.write(data) if isinstance(written, int): assert written == len(data) def cmd(self, cmd, enable): + """Execute a command. + + Args: + cmd (str): Command to execute. One of (``RESET``, ``TRIGGER``, + ``ARM``, ``DCM``, ``START``). + enable (bool): Enable (``True``) or disable (``False``) the + feature. + """ cmd = self._commands.index(cmd) << 1 if not enable: cmd |= 1 self.write(struct.pack("cb", self._escape, cmd)) def write_mem(self, channel, data, start_addr=0): + """Write to channel memory. + + Args: + channel (int): Channel index to write to. Assumes every board in + the stack has :attr:`num_dacs` DAC outputs. + data (bytes): Data to write to memory. + start_addr (int): Start address to write data to. + """ board, dac = divmod(channel, self.num_dacs) data = struct.pack(" TBool: if rid is None: rid = self.rid return self._check_pause(rid) + _submit = staticmethod(make_parent_action("scheduler_submit")) + def submit(self, pipeline_name=None, expid=None, priority=None, due_date=None, flush=False): + if pipeline_name is None: + pipeline_name = self.pipeline_name + if expid is None: + expid = self.expid + if priority is None: + priority = self.priority + return self._submit(pipeline_name, expid, priority, due_date, flush) + + delete = staticmethod(make_parent_action("scheduler_delete")) + request_termination = staticmethod( + make_parent_action("scheduler_request_termination")) + get_status = staticmethod(make_parent_action("scheduler_get_status")) + class CCB: issue = staticmethod(make_parent_action("ccb_issue")) @@ -238,7 +245,7 @@ def main(): elif action == "write_results": filename = "{:09}-{}.h5".format(rid, exp.__name__) with h5py.File(filename, "w") as f: - dataset_mgr.write_hdf5(f.create_group("datasets")) + dataset_mgr.write_hdf5(f) f["artiq_version"] = artiq_version f["rid"] = rid f["start_time"] = int(time.mktime(start_time)) diff --git a/artiq/runtime.rs/Cargo.lock b/artiq/runtime.rs/Cargo.lock index 6e5a7538d..eb4659fcb 100644 --- a/artiq/runtime.rs/Cargo.lock +++ b/artiq/runtime.rs/Cargo.lock @@ -2,15 +2,24 @@ name = "runtime" version = "0.0.0" dependencies = [ + "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "fringe 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log_buffer 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "lwip 0.0.0", "std_artiq 0.0.0", + "walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "alloc_artiq" version = "0.0.0" +[[package]] +name = "byteorder" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fringe" version = "1.1.0" @@ -19,11 +28,30 @@ dependencies = [ "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "libc" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "log" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log_buffer" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "lwip" version = "0.0.0" @@ -43,6 +71,32 @@ dependencies = [ "alloc_artiq 0.0.0", ] +[[package]] +name = "walkdir" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [metadata] +"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" "checksum fringe 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "987689dcfad85eee8d76b477865641ec483e63fb86d52966bfc350c4a647d78a" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2" +"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" +"checksum log_buffer 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec57723b84bbe7bdf76aa93169c9b59e67473317c6de3a83cb2a0f8ccb2aa493" +"checksum walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c66c0b9792f0a765345452775f3adbd28dde9d33f30d13e5dcc5ae17cf6f3780" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/artiq/runtime.rs/Cargo.toml b/artiq/runtime.rs/Cargo.toml index 962e076cb..5a459d204 100644 --- a/artiq/runtime.rs/Cargo.toml +++ b/artiq/runtime.rs/Cargo.toml @@ -2,16 +2,23 @@ authors = ["The ARTIQ Project Developers"] name = "runtime" version = "0.0.0" +build = "build.rs" + +[build-dependencies] +walkdir = "0.1" [lib] -name = "artiq_rust" +name = "runtime" crate-type = ["staticlib"] path = "src/lib.rs" [dependencies] -std_artiq = { path = "libstd_artiq" } +std_artiq = { path = "libstd_artiq", features = ["alloc"] } +lwip = { path = "liblwip", default-features = false } fringe = { version = "= 1.1.0", default-features = false, features = ["alloc"] } -lwip = { path = "liblwip" } +log = { version = "0.3", default-features = false, features = ["max_level_debug"] } +log_buffer = { version = "1.0" } +byteorder = { version = "0.5", default-features = false } [profile.dev] panic = 'abort' diff --git a/artiq/runtime.rs/build.rs b/artiq/runtime.rs/build.rs new file mode 100644 index 000000000..e545ea3d4 --- /dev/null +++ b/artiq/runtime.rs/build.rs @@ -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 { + 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 + }) +} diff --git a/artiq/runtime.rs/libksupport/Cargo.lock b/artiq/runtime.rs/libksupport/Cargo.lock new file mode 100644 index 000000000..df5a6a1bb --- /dev/null +++ b/artiq/runtime.rs/libksupport/Cargo.lock @@ -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" diff --git a/artiq/runtime.rs/libksupport/Cargo.toml b/artiq/runtime.rs/libksupport/Cargo.toml new file mode 100644 index 000000000..9ba7d5f65 --- /dev/null +++ b/artiq/runtime.rs/libksupport/Cargo.toml @@ -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 diff --git a/artiq/runtime.rs/libksupport/api.rs b/artiq/runtime.rs/libksupport/api.rs new file mode 100644 index 000000000..aa6483bbd --- /dev/null +++ b/artiq/runtime.rs/libksupport/api.rs @@ -0,0 +1,121 @@ +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!(lround), + + /* 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), + +// #if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0)) + api!(dds_init), + api!(dds_init_sync), + api!(dds_batch_enter), + api!(dds_batch_exit), + api!(dds_set), +// #endif + + api!(i2c_init), + api!(i2c_start), + api!(i2c_stop), + api!(i2c_write), + api!(i2c_read), +]; diff --git a/artiq/runtime.rs/libksupport/dyld.rs b/artiq/runtime.rs/libksupport/dyld.rs new file mode 100644 index 000000000..c73653a19 --- /dev/null +++ b/artiq/runtime.rs/libksupport/dyld.rs @@ -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(shlib: &[u8], base: usize, mut resolve: F) + -> Result + where F: Fn(&str) -> usize { + extern fn wrapper(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::, &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 + } +} diff --git a/artiq/runtime.rs/libksupport/lib.rs b/artiq/runtime.rs/libksupport/lib.rs new file mode 100644 index 000000000..7e4d66b59 --- /dev/null +++ b/artiq/runtime.rs/libksupport/lib.rs @@ -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: F) -> R { + while mailbox::receive() == 0 {} + let result = f(unsafe { mem::transmute::(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::(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) +} diff --git a/artiq/runtime.rs/liblwip-sys/lib.rs b/artiq/runtime.rs/liblwip-sys/lib.rs index aaac1d424..82a7c5bcf 100644 --- a/artiq/runtime.rs/liblwip-sys/lib.rs +++ b/artiq/runtime.rs/liblwip-sys/lib.rs @@ -103,6 +103,10 @@ pub struct udp_pcb { pub const TCP_WRITE_FLAG_COPY: u8 = 0x01; pub const TCP_WRITE_FLAG_MORE: u8 = 0x02; +pub const SOF_REUSEADDR: u8 = 0x04; +pub const SOF_KEEPALIVE: u8 = 0x08; +pub const SOF_BROADCAST: u8 = 0x20; + extern { pub fn pbuf_alloc(l: pbuf_layer, length: u16, type_: pbuf_type) -> *mut pbuf; pub fn pbuf_realloc(p: *mut pbuf, length: u16); @@ -122,28 +126,29 @@ extern { pub fn tcp_bind(pcb: *mut tcp_pcb, ipaddr: *mut ip_addr, port: u16) -> err; pub fn tcp_listen_with_backlog(pcb: *mut tcp_pcb, backlog: u8) -> *mut tcp_pcb; pub fn tcp_accept(pcb: *mut tcp_pcb, - accept: extern fn(arg: *mut c_void, newpcb: *mut tcp_pcb, - err: err) -> err); + accept: Option err>); pub fn tcp_connect(pcb: *mut tcp_pcb, ipaddr: *mut ip_addr, port: u16, connected: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, err: err)) -> err; pub fn tcp_write(pcb: *mut tcp_pcb, dataptr: *const c_void, len: u16, apiflags: u8) -> err; pub fn tcp_sent(pcb: *mut tcp_pcb, - sent: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, len: u16) -> err); + sent: Option err>); pub fn tcp_recv(pcb: *mut tcp_pcb, - recv: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, p: *mut pbuf, - err: err) -> err); + recv: Option err>); pub fn tcp_recved(pcb: *mut tcp_pcb, len: u16); pub fn tcp_poll(pcb: *mut tcp_pcb, - poll: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb), + poll: Option, interval: u8); pub fn tcp_shutdown(pcb: *mut tcp_pcb, shut_rx: c_int, shut_tx: c_int) -> err; pub fn tcp_close(pcb: *mut tcp_pcb) -> err; pub fn tcp_abort(pcb: *mut tcp_pcb); pub fn tcp_err(pcb: *mut tcp_pcb, - err: extern fn(arg: *mut c_void, err: err)); + err: Option); // nonstandard pub fn tcp_sndbuf_(pcb: *mut tcp_pcb) -> u16; + pub fn tcp_so_options_(pcb: *mut tcp_pcb) -> *mut u8; pub fn udp_new() -> *mut udp_pcb; pub fn udp_new_ip_type(type_: ip_addr_type) -> *mut udp_pcb; @@ -154,7 +159,7 @@ extern { pub fn udp_send(pcb: *mut udp_pcb, p: *mut pbuf) -> err; pub fn udp_sendto(pcb: *mut udp_pcb, p: *mut pbuf, ipaddr: *mut ip_addr, port: u16) -> err; pub fn udp_recv(pcb: *mut udp_pcb, - recv: extern fn(arg: *mut c_void, upcb: *mut udp_pcb, p: *mut pbuf, - addr: *mut ip_addr, port: u16), + recv: Option, recv_arg: *mut c_void); } diff --git a/artiq/runtime.rs/liblwip/Cargo.toml b/artiq/runtime.rs/liblwip/Cargo.toml index cd76fe969..8d00166eb 100644 --- a/artiq/runtime.rs/liblwip/Cargo.toml +++ b/artiq/runtime.rs/liblwip/Cargo.toml @@ -9,4 +9,8 @@ path = "lib.rs" [dependencies] lwip-sys = { path = "../liblwip-sys" } -std_artiq = { path = "../libstd_artiq" } +std_artiq = { path = "../libstd_artiq", features = ["alloc"] } + +[features] +default = ["preemption"] +preemption = [] diff --git a/artiq/runtime.rs/liblwip/lib.rs b/artiq/runtime.rs/liblwip/lib.rs index 49af078a0..f94b65dac 100644 --- a/artiq/runtime.rs/liblwip/lib.rs +++ b/artiq/runtime.rs/liblwip/lib.rs @@ -8,7 +8,9 @@ extern crate lwip_sys; extern crate std_artiq as std; use core::marker::PhantomData; +use core::ptr; use core::cell::RefCell; +use core::fmt; use alloc::boxed::Box; use collections::LinkedList; use libc::c_void; @@ -32,6 +34,8 @@ pub enum Error { ConnectionReset, ConnectionClosed, IllegalArgument, + // Not used by lwip; added for building blocking interfaces. + Interrupted } impl Error { @@ -53,6 +57,7 @@ impl Error { Error::ConnectionReset => "connection reset", Error::ConnectionClosed => "connection closed", Error::IllegalArgument => "illegal argument", + Error::Interrupted => "interrupted" } } } @@ -71,7 +76,12 @@ impl error::Error for Error { impl From for std::io::Error { fn from(lower: Error) -> std::io::Error { - std::io::Error::new(std::io::ErrorKind::Other, lower) + use std::io; + + match lower { + Error::Interrupted => io::Error::new(io::ErrorKind::Interrupted, "interrupted"), + err => io::Error::new(io::ErrorKind::Other, err) + } } } @@ -102,19 +112,54 @@ fn result_from(err: lwip_sys::err, f: F) -> Result #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum IpAddr { - Ip4([u8; 4]), - Ip6([u16; 8]), - IpAny + V4([u8; 4]), + V6([u16; 8]), + Any } -pub const IP4_ANY: IpAddr = IpAddr::Ip4([0, 0, 0, 0]); -pub const IP6_ANY: IpAddr = IpAddr::Ip6([0, 0, 0, 0, 0, 0, 0, 0]); -pub const IP_ANY: IpAddr = IpAddr::IpAny; +pub const IP4_ANY: IpAddr = IpAddr::V4([0, 0, 0, 0]); +pub const IP6_ANY: IpAddr = IpAddr::V6([0, 0, 0, 0, 0, 0, 0, 0]); +pub const IP_ANY: IpAddr = IpAddr::Any; + +impl fmt::Display for IpAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + IpAddr::V4(ref octets) => + write!(f, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]), + + IpAddr::V6(ref segments) => { + #[derive(Clone, Copy, PartialEq, Eq)] + enum State { Head, Skip, Tail }; + + let mut state = State::Head; + for (idx, &segment) in segments.iter().enumerate() { + match state { + State::Head | State::Skip if segment == 0 => + state = State::Skip, + State::Skip if segment != 0 => { + state = State::Tail; + try!(write!(f, ":{:x}", segment)) + } + _ => try!(write!(f, "{:x}", segment)) + } + + if state != State::Skip && idx != 15 { + try!(write!(f, ":")) + } + } + Ok(()) + }, + + IpAddr::Any => + write!(f, "*") + } + } +} impl IpAddr { fn into_raw(self) -> lwip_sys::ip_addr { match self { - IpAddr::Ip4(ref octets) => + IpAddr::V4(octets) => lwip_sys::ip_addr { data: [(octets[0] as u32) << 24 | (octets[1] as u32) << 16 | @@ -123,7 +168,7 @@ impl IpAddr { 0, 0, 0], type_: lwip_sys::IPADDR_TYPE_V4 }, - IpAddr::Ip6(ref segments) => + IpAddr::V6(segments) => lwip_sys::ip_addr { data: [(segments[0] as u32) << 16 | (segments[1] as u32), (segments[2] as u32) << 16 | (segments[3] as u32), @@ -131,7 +176,7 @@ impl IpAddr { (segments[6] as u32) << 16 | (segments[7] as u32)], type_: lwip_sys::IPADDR_TYPE_V6 }, - IpAddr::IpAny => + IpAddr::Any => lwip_sys::ip_addr { data: [0; 4], type_: lwip_sys::IPADDR_TYPE_ANY @@ -142,17 +187,17 @@ impl IpAddr { unsafe fn from_raw(raw: *mut lwip_sys::ip_addr) -> IpAddr { match *raw { lwip_sys::ip_addr { type_: lwip_sys::IPADDR_TYPE_V4, data } => - IpAddr::Ip4([(data[0] >> 24) as u8, + IpAddr::V4([(data[0] >> 24) as u8, (data[0] >> 16) as u8, (data[0] >> 8) as u8, (data[0] >> 0) as u8]), lwip_sys::ip_addr { type_: lwip_sys::IPADDR_TYPE_V6, data } => - IpAddr::Ip6([(data[0] >> 16) as u16, data[0] as u16, + IpAddr::V6([(data[0] >> 16) as u16, data[0] as u16, (data[1] >> 16) as u16, data[1] as u16, (data[2] >> 16) as u16, data[2] as u16, (data[3] >> 16) as u16, data[3] as u16]), lwip_sys::ip_addr { type_: lwip_sys::IPADDR_TYPE_ANY, .. } => - IpAddr::IpAny + IpAddr::Any } } } @@ -163,6 +208,12 @@ pub struct SocketAddr { pub port: u16 } +impl fmt::Display for SocketAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}:{}", self.ip, self.port) + } +} + impl SocketAddr { pub fn new(ip: IpAddr, port: u16) -> SocketAddr { SocketAddr { ip: ip, port: port } @@ -175,6 +226,9 @@ pub struct Pbuf<'payload> { phantom: PhantomData<&'payload [u8]> } +#[cfg(not(feature = "preemption"))] +unsafe impl<'payload> Send for Pbuf<'payload> {} + impl<'payload> Pbuf<'payload> { unsafe fn from_raw(raw: *mut lwip_sys::pbuf) -> Pbuf<'payload> { Pbuf { raw: raw, phantom: PhantomData } @@ -259,6 +313,9 @@ pub struct UdpSocket { state: Box> } +#[cfg(not(feature = "preemption"))] +unsafe impl Send for UdpSocket {} + impl UdpSocket { pub fn new() -> Result { extern fn recv(arg: *mut c_void, _pcb: *mut lwip_sys::udp_pcb, @@ -279,12 +336,12 @@ impl UdpSocket { recv_buffer: LinkedList::new() })); let arg = &mut *state as *mut RefCell as *mut _; - lwip_sys::udp_recv(raw, recv, arg); + lwip_sys::udp_recv(raw, Some(recv), arg); Ok(UdpSocket { raw: raw, state: state }) } } - pub fn state(&self) -> *const RefCell { + pub fn state(&self) -> &RefCell { &*self.state } @@ -347,6 +404,9 @@ pub struct TcpListener { state: Box> } +#[cfg(not(feature = "preemption"))] +unsafe impl Send for TcpListener {} + impl TcpListener { pub fn bind(addr: SocketAddr) -> Result { 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 as *mut _; lwip_sys::tcp_arg(raw2, arg); - lwip_sys::tcp_accept(raw2, accept); + lwip_sys::tcp_accept(raw2, Some(accept)); Ok(TcpListener { raw: raw2, state: state }) } } - pub fn state(&self) -> *const RefCell { + pub fn state(&self) -> &RefCell { &*self.state } pub fn try_accept(&self) -> Option { self.state.borrow_mut().backlog.pop_front() } + + pub fn keepalive(&self) -> bool { + unsafe { *lwip_sys::tcp_so_options_(self.raw) & lwip_sys::SOF_KEEPALIVE != 0 } + } + + pub fn set_keepalive(&self, keepalive: bool) { + if keepalive { + unsafe { *lwip_sys::tcp_so_options_(self.raw) |= lwip_sys::SOF_KEEPALIVE } + } else { + unsafe { *lwip_sys::tcp_so_options_(self.raw) &= !lwip_sys::SOF_KEEPALIVE } + } + } } impl Drop for TcpListener { @@ -409,7 +481,8 @@ pub enum Shutdown { #[derive(Debug)] pub struct TcpStreamState { recv_buffer: LinkedList>>, - send_avail: usize + send_avail: usize, + total_sent: usize } impl TcpStreamState { @@ -428,6 +501,9 @@ pub struct TcpStream { state: Box> } +#[cfg(not(feature = "preemption"))] +unsafe impl Send for TcpStream {} + impl TcpStream { fn from_raw(raw: *mut lwip_sys::tcp_pcb) -> TcpStream { extern fn recv(arg: *mut c_void, _raw: *mut lwip_sys::tcp_pcb, @@ -445,10 +521,12 @@ impl TcpStream { } extern fn sent(arg: *mut c_void, raw: *mut lwip_sys::tcp_pcb, - _len: u16) -> lwip_sys::err { + len: u16) -> lwip_sys::err { unsafe { let state = arg as *mut RefCell; - (*state).borrow_mut().send_avail = lwip_sys::tcp_sndbuf_(raw) as usize; + let mut state = (*state).borrow_mut(); + state.send_avail = lwip_sys::tcp_sndbuf_(raw) as usize; + state.total_sent = state.total_sent.wrapping_add(len as usize); } lwip_sys::ERR_OK } @@ -463,37 +541,55 @@ impl TcpStream { unsafe { let mut state = Box::new(RefCell::new(TcpStreamState { recv_buffer: LinkedList::new(), - send_avail: lwip_sys::tcp_sndbuf_(raw) as usize + send_avail: lwip_sys::tcp_sndbuf_(raw) as usize, + total_sent: 0 })); let arg = &mut *state as *mut RefCell as *mut _; lwip_sys::tcp_arg(raw, arg); - lwip_sys::tcp_recv(raw, recv); - lwip_sys::tcp_sent(raw, sent); - lwip_sys::tcp_err(raw, err); + lwip_sys::tcp_recv(raw, Some(recv)); + lwip_sys::tcp_sent(raw, Some(sent)); + lwip_sys::tcp_err(raw, Some(err)); TcpStream { raw: raw, state: state } } } - pub fn state(&self) -> *const RefCell { + pub fn state(&self) -> &RefCell { &*self.state } - pub fn write(&self, data: &[u8]) -> Result { - let sndbuf = unsafe { lwip_sys::tcp_sndbuf_(self.raw) } as usize; + unsafe fn write_common(&self, data: &[u8], copy: bool) -> Result { + let sndbuf = lwip_sys::tcp_sndbuf_(self.raw) as usize; let len = if data.len() < sndbuf { data.len() } else { sndbuf }; - let result = result_from(unsafe { + let result = result_from({ lwip_sys::tcp_write(self.raw, data as *const [u8] as *const _, len as u16, - lwip_sys::TCP_WRITE_FLAG_COPY | - lwip_sys::TCP_WRITE_FLAG_MORE) + lwip_sys::TCP_WRITE_FLAG_MORE | + if copy { lwip_sys::TCP_WRITE_FLAG_COPY } else { 0 }) }, || len); - self.state.borrow_mut().send_avail = unsafe { lwip_sys::tcp_sndbuf_(self.raw) } as usize; + self.state.borrow_mut().send_avail = lwip_sys::tcp_sndbuf_(self.raw) as usize; result } + pub fn write(&self, data: &[u8]) -> Result { + unsafe { self.write_common(data, true) } + } + + pub fn write_in_place(&self, data: &[u8], mut relinquish: F) -> Result + where F: FnMut() -> Result<()> { + let cursor = self.state.borrow().total_sent; + let written = try!(unsafe { self.write_common(data, false) }); + loop { + let cursor_now = self.state.borrow().total_sent; + if cursor_now >= cursor.wrapping_add(written) { + return Ok(written) + } else { + try!(relinquish()) + } + } + } + pub fn flush(&self) -> Result<()> { - const EMPTY_DATA: [u8; 0] = []; result_from(unsafe { - lwip_sys::tcp_write(self.raw, &EMPTY_DATA as *const [u8] as *const _, 0, 0) + lwip_sys::tcp_write(self.raw, ptr::null(), 0, 0) }, || ()) } @@ -505,7 +601,10 @@ impl TcpStream { Some(_) => () } match state.recv_buffer.pop_front() { - Some(Ok(pbuf)) => return Ok(Some(pbuf)), + Some(Ok(pbuf)) => { + unsafe { lwip_sys::tcp_recved(self.raw, pbuf.len() as u16) } + return Ok(Some(pbuf)) + }, _ => unreachable!() } } @@ -533,6 +632,11 @@ impl TcpStream { impl Drop for TcpStream { fn drop(&mut self) { unsafe { + // lwip *will* try to call back after tcp_close + lwip_sys::tcp_recv(self.raw, None); + lwip_sys::tcp_sent(self.raw, None); + lwip_sys::tcp_err(self.raw, None); + // tcp_close can fail here, but in drop() we don't care let _ = lwip_sys::tcp_close(self.raw); } diff --git a/artiq/runtime.rs/libstd_artiq/Cargo.toml b/artiq/runtime.rs/libstd_artiq/Cargo.toml index dfd38df4a..a6435b1f5 100644 --- a/artiq/runtime.rs/libstd_artiq/Cargo.toml +++ b/artiq/runtime.rs/libstd_artiq/Cargo.toml @@ -9,3 +9,6 @@ path = "lib.rs" [dependencies] alloc_artiq = { path = "../liballoc_artiq" } + +[features] +alloc = [] diff --git a/artiq/runtime.rs/libstd_artiq/io/buffered.rs b/artiq/runtime.rs/libstd_artiq/io/buffered.rs index e23b74ff1..3088e4e63 100644 --- a/artiq/runtime.rs/libstd_artiq/io/buffered.rs +++ b/artiq/runtime.rs/libstd_artiq/io/buffered.rs @@ -17,6 +17,8 @@ use core::cmp; use core::fmt; use io::{self, DEFAULT_BUF_SIZE, Error, ErrorKind, SeekFrom}; use io::memchr; +use alloc::boxed::Box; +use collections::vec::Vec; /// The `BufReader` struct adds buffering to any reader. /// diff --git a/artiq/runtime.rs/libstd_artiq/io/cursor.rs b/artiq/runtime.rs/libstd_artiq/io/cursor.rs index 1db50ee5b..88e7646d5 100644 --- a/artiq/runtime.rs/libstd_artiq/io/cursor.rs +++ b/artiq/runtime.rs/libstd_artiq/io/cursor.rs @@ -13,6 +13,8 @@ use io::prelude::*; use core::cmp; use io::{self, SeekFrom, Error, ErrorKind}; +use alloc::boxed::Box; +use collections::vec::Vec; /// A `Cursor` wraps another type and provides it with a /// [`Seek`](trait.Seek.html) implementation. diff --git a/artiq/runtime.rs/libstd_artiq/io/error.rs b/artiq/runtime.rs/libstd_artiq/io/error.rs index f8684a4ec..37ecc4507 100644 --- a/artiq/runtime.rs/libstd_artiq/io/error.rs +++ b/artiq/runtime.rs/libstd_artiq/io/error.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - use alloc::boxed::Box; +#[cfg(feature="alloc")] use alloc::boxed::Box; +#[cfg(not(feature="alloc"))] use ::FakeBox as Box; use core::convert::Into; use core::fmt; use core::marker::{Send, Sync}; @@ -62,13 +63,19 @@ pub struct Error { enum Repr { Os(i32), + #[cfg(feature="alloc")] Custom(Box), + #[cfg(not(feature="alloc"))] + Custom(Custom), } #[derive(Debug)] struct Custom { kind: ErrorKind, + #[cfg(feature="alloc")] error: Box, + #[cfg(not(feature="alloc"))] + error: &'static str } /// A list specifying general categories of I/O error. @@ -162,12 +169,21 @@ impl Error { /// // errors can also be created from other errors /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); /// ``` + #[cfg(feature="alloc")] pub fn new(kind: ErrorKind, error: E) -> Error where E: Into> { Self::_new(kind, error.into()) } + #[cfg(not(feature="alloc"))] + pub fn new(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 { repr: Repr::Custom(Box::new(Custom { @@ -177,6 +193,16 @@ impl Error { } } + #[cfg(not(feature="alloc"))] + fn _new(kind: ErrorKind, error: &'static str) -> Error { + Error { + repr: Repr::Custom(Box::new(Custom { + kind: kind, + error: error, + })) + } + } + /// Creates a new instance of an `Error` from a particular OS error code. pub fn from_raw_os_error(code: i32) -> Error { Error { repr: Repr::Os(code) } @@ -198,6 +224,7 @@ impl Error { /// /// If this `Error` was constructed via `new` then this function will /// return `Some`, otherwise it will return `None`. + #[cfg(feature="alloc")] pub fn get_ref(&self) -> Option<&(error::Error+Send+Sync+'static)> { match self.repr { Repr::Os(..) => None, @@ -210,6 +237,7 @@ impl Error { /// /// If this `Error` was constructed via `new` then this function will /// return `Some`, otherwise it will return `None`. + #[cfg(feature="alloc")] pub fn get_mut(&mut self) -> Option<&mut (error::Error+Send+Sync+'static)> { match self.repr { Repr::Os(..) => None, @@ -221,6 +249,7 @@ impl Error { /// /// If this `Error` was constructed via `new` then this function will /// return `Some`, otherwise it will return `None`. + #[cfg(feature="alloc")] pub fn into_inner(self) -> Option> { match self.repr { Repr::Os(..) => None, @@ -282,14 +311,24 @@ impl error::Error for Error { ErrorKind::UnexpectedEof => "unexpected end of file", ErrorKind::__Nonexhaustive => unreachable!() }, - Repr::Custom(ref c) => c.error.description(), + Repr::Custom(ref c) => { + #[cfg(feature="alloc")] + { c.error.description() } + #[cfg(not(feature="alloc"))] + { c.error } + }, } } fn cause(&self) -> Option<&error::Error> { match self.repr { Repr::Os(..) => None, - Repr::Custom(ref c) => c.error.cause(), + Repr::Custom(ref _c) => { + #[cfg(feature="alloc")] + { _c.error.cause() } + #[cfg(not(feature="alloc"))] + { None } + } } } } diff --git a/artiq/runtime.rs/libstd_artiq/io/prelude.rs b/artiq/runtime.rs/libstd_artiq/io/prelude.rs index eae5acf07..58df71e69 100644 --- a/artiq/runtime.rs/libstd_artiq/io/prelude.rs +++ b/artiq/runtime.rs/libstd_artiq/io/prelude.rs @@ -20,6 +20,3 @@ pub use super::{Read, Write, Seek}; pub use super::BufRead; - - pub use alloc::boxed::Box; - pub use collections::vec::Vec; diff --git a/artiq/runtime.rs/libstd_artiq/lib.rs b/artiq/runtime.rs/libstd_artiq/lib.rs index 1ec357302..648baa61e 100644 --- a/artiq/runtime.rs/libstd_artiq/lib.rs +++ b/artiq/runtime.rs/libstd_artiq/lib.rs @@ -1,6 +1,7 @@ #![feature(lang_items, asm, alloc, collections, libc, needs_panic_runtime, question_mark, unicode, reflect_marker, raw, int_error_internals, - try_from, try_borrow)] + try_from, try_borrow, macro_reexport, allow_internal_unstable, + stmt_expr_attributes)] #![no_std] #![needs_panic_runtime] @@ -8,6 +9,7 @@ extern crate rustc_unicode; extern crate alloc_artiq; extern crate alloc; #[macro_use] +#[macro_reexport(vec, format)] extern crate collections; extern crate libc; @@ -21,62 +23,22 @@ pub use collections::{binary_heap, borrow, boxed, btree_map, btree_set, fmt, lin pub mod prelude { pub mod v1 { pub use core::prelude::v1::*; - pub use collections::*; - pub use io::{Read, Write, Seek}; - pub use io::BufRead; + pub use collections::boxed::Box; + pub use collections::borrow::ToOwned; + pub use collections::string::{String, ToString}; + pub use collections::vec::Vec; } } -pub mod time; pub mod error; pub mod io; -use core::fmt::Write; - -#[macro_export] -macro_rules! print { - ($($arg:tt)*) => ($crate::print_fmt(format_args!($($arg)*))); -} - -#[macro_export] -macro_rules! println { - ($fmt:expr) => (print!(concat!($fmt, "\n"))); - ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); -} - -extern { - fn putchar(c: libc::c_int) -> libc::c_int; - fn readchar() -> libc::c_char; -} - -pub struct Console; - -impl core::fmt::Write for Console { - fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { - for c in s.bytes() { unsafe { putchar(c as i32); } } - Ok(()) +// Provide Box::new wrapper +#[cfg(not(feature="alloc"))] +struct FakeBox(core::marker::PhantomData); +#[cfg(not(feature="alloc"))] +impl FakeBox { + fn new(val: T) -> T { + val } } - -pub fn print_fmt(args: self::core::fmt::Arguments) { - let _ = Console.write_fmt(args); -} - -#[lang = "panic_fmt"] -extern fn panic_fmt(args: self::core::fmt::Arguments, file: &'static str, line: u32) -> ! { - let _ = write!(Console, "panic at {}:{}: ", file, line); - let _ = Console.write_fmt(args); - let _ = write!(Console, "\nwaiting for debugger...\n"); - unsafe { - let _ = readchar(); - loop { asm!("l.trap 0") } - } -} - -// Allow linking with crates that are built as -Cpanic=unwind even when the root crate -// is built with -Cpanic=abort. -#[allow(non_snake_case)] -#[no_mangle] -pub extern "C" fn _Unwind_Resume() -> ! { - loop {} -} diff --git a/artiq/runtime.rs/libstd_artiq/time/duration.rs b/artiq/runtime.rs/libstd_artiq/time/duration.rs deleted file mode 100644 index c3d7bd1ed..000000000 --- a/artiq/runtime.rs/libstd_artiq/time/duration.rs +++ /dev/null @@ -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 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 for Duration { - type Output = Duration; - - fn div(self, rhs: u32) -> Duration { - Duration { - millis: self.millis / (rhs as u64) - } - } -} diff --git a/artiq/runtime.rs/libstd_artiq/time/instant.rs b/artiq/runtime.rs/libstd_artiq/time/instant.rs deleted file mode 100644 index 2282b0f5d..000000000 --- a/artiq/runtime.rs/libstd_artiq/time/instant.rs +++ /dev/null @@ -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 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 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") - } - } -} diff --git a/artiq/runtime.rs/libstd_artiq/time/mod.rs b/artiq/runtime.rs/libstd_artiq/time/mod.rs deleted file mode 100644 index c1269983d..000000000 --- a/artiq/runtime.rs/libstd_artiq/time/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub use self::duration::Duration; -pub use self::instant::Instant; - -mod duration; -mod instant; diff --git a/artiq/runtime.rs/src/analyzer.rs b/artiq/runtime.rs/src/analyzer.rs new file mode 100644 index 000000000..ea126512e --- /dev/null +++ b/artiq/runtime.rs/src/analyzer.rs @@ -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::() == 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) + } + } +} diff --git a/artiq/runtime.rs/src/analyzer_proto.rs b/artiq/runtime.rs/src/analyzer_proto.rs new file mode 100644 index 000000000..344882d3b --- /dev/null +++ b/artiq/runtime.rs/src/analyzer_proto.rs @@ -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(()) + } +} diff --git a/artiq/runtime.rs/src/board.rs b/artiq/runtime.rs/src/board.rs new file mode 100644 index 000000000..c73ffd0f5 --- /dev/null +++ b/artiq/runtime.rs/src/board.rs @@ -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]) + } +} diff --git a/artiq/runtime.rs/src/cache.rs b/artiq/runtime.rs/src/cache.rs new file mode 100644 index 000000000..4136b96f3 --- /dev/null +++ b/artiq/runtime.rs/src/cache.rs @@ -0,0 +1,53 @@ +use std::vec::Vec; +use std::string::String; +use std::btree_map::BTreeMap; + +#[derive(Debug)] +struct Entry { + data: Vec, + borrowed: bool +} + +#[derive(Debug)] +pub struct Cache { + entries: BTreeMap +} + +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; + } + } +} diff --git a/artiq/runtime.rs/src/clock.rs b/artiq/runtime.rs/src/clock.rs new file mode 100644 index 000000000..3c203a196 --- /dev/null +++ b/artiq/runtime.rs/src/clock.rs @@ -0,0 +1,76 @@ +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_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 { + 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) + } +} diff --git a/artiq/runtime.rs/src/config.rs b/artiq/runtime.rs/src/config.rs new file mode 100644 index 000000000..df3d1868e --- /dev/null +++ b/artiq/runtime.rs/src/config.rs @@ -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 { + 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 { + 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() } +} diff --git a/artiq/runtime.rs/src/kernel.rs b/artiq/runtime.rs/src/kernel.rs new file mode 100644 index 000000000..5d7e17696 --- /dev/null +++ b/artiq/runtime.rs/src/kernel.rs @@ -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 +} diff --git a/artiq/runtime.rs/src/kernel_proto.rs b/artiq/runtime.rs/src/kernel_proto.rs new file mode 100644 index 000000000..afaf7c35e --- /dev/null +++ b/artiq/runtime.rs/src/kernel_proto.rs @@ -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>), + + 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::*; diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index 04e987129..e661c427f 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -1,47 +1,142 @@ #![no_std] +#![feature(libc, const_fn, try_borrow, stmt_expr_attributes, repr_simd, asm, + lang_items)] #[macro_use] extern crate std_artiq as std; +extern crate libc; +#[macro_use] +extern crate log; +extern crate log_buffer; +extern crate byteorder; +extern crate fringe; +extern crate lwip; -use std::prelude::v1::*; +use core::fmt::Write; +use logger::BufferLogger; -pub mod io; +extern { + fn putchar(c: libc::c_int) -> libc::c_int; + fn readchar() -> libc::c_char; +} + +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::print_fmt(format_args!($($arg)*))); +} + +#[macro_export] +macro_rules! println { + ($fmt:expr) => (print!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); +} + +pub struct Console; + +impl core::fmt::Write for Console { + fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { + for c in s.bytes() { unsafe { putchar(c as i32); } } + Ok(()) + } +} + +pub fn print_fmt(args: self::core::fmt::Arguments) { + let _ = Console.write_fmt(args); +} + +#[lang = "panic_fmt"] +extern fn panic_fmt(args: self::core::fmt::Arguments, file: &'static str, line: u32) -> ! { + let _ = write!(Console, "panic at {}:{}: {}\n", file, line, args); + let _ = write!(Console, "waiting for debugger...\n"); + unsafe { + let _ = readchar(); + loop { asm!("l.trap 0") } + } +} + +mod board; +mod config; +mod clock; +mod rtio_crg; +mod mailbox; +mod rpc_queue; + +mod urc; +mod sched; +mod logger; +mod cache; + +mod proto; +mod kernel_proto; +mod session_proto; +mod moninj_proto; +mod analyzer_proto; +mod rpc_proto; + +mod kernel; +mod session; +mod moninj; +#[cfg(has_rtio_analyzer)] +mod analyzer; extern { fn network_init(); fn lwip_service(); } -fn timer(waiter: io::Waiter) { - loop { - println!("tick"); - waiter.sleep(std::time::Duration::from_millis(1000)).unwrap(); - } -} +include!(concat!(env!("OUT_DIR"), "/git_info.rs")); -fn echo(waiter: io::Waiter) { - let addr = io::SocketAddr::new(io::IP_ANY, 1234); - let listener = io::TcpListener::bind(waiter, addr).unwrap(); - loop { - let (mut stream, _addr) = listener.accept().unwrap(); - loop { - let mut buf = [0]; - stream.read(&mut buf).unwrap(); - stream.write(&buf).unwrap(); - } - } +// Allow linking with crates that are built as -Cpanic=unwind even if we use -Cpanic=abort. +// This is never called. +#[allow(non_snake_case)] +#[no_mangle] +pub extern "C" fn _Unwind_Resume() -> ! { + loop {} } #[no_mangle] pub unsafe extern fn rust_main() { - println!("Accepting network sessions in Rust."); - network_init(); + static mut LOG_BUFFER: [u8; 4096] = [0; 4096]; + BufferLogger::new(&mut LOG_BUFFER[..]) + .register(move || { + info!("booting ARTIQ..."); + info!("software version {}", GIT_COMMIT); + info!("gateware version {}", ::board::ident(&mut [0; 64])); - let mut scheduler = io::Scheduler::new(); - scheduler.spawn(4096, timer); - scheduler.spawn(4096, echo); - loop { - lwip_service(); - scheduler.run() + clock::init(); + rtio_crg::init(); + network_init(); + + let mut scheduler = sched::Scheduler::new(); + scheduler.spawner().spawn(16384, session::thread); + scheduler.spawner().spawn(4096, moninj::thread); + #[cfg(has_rtio_analyzer)] + scheduler.spawner().spawn(4096, analyzer::thread); + + loop { + scheduler.run(); + lwip_service(); + } + }) +} + +#[no_mangle] +pub unsafe extern fn isr() { + use board::{irq, csr}; + extern { fn uart_isr(); } + + let irqs = irq::pending() & irq::get_mask(); + if irqs & (1 << csr::UART_INTERRUPT) != 0 { + uart_isr() } } + +#[no_mangle] +pub fn sys_now() -> u32 { + clock::get_ms() as u32 +} + +#[no_mangle] +pub fn sys_jiffies() -> u32 { + clock::get_ms() as u32 +} diff --git a/artiq/runtime.rs/src/logger.rs b/artiq/runtime.rs/src/logger.rs new file mode 100644 index 000000000..d09995bf7 --- /dev/null +++ b/artiq/runtime.rs/src/logger.rs @@ -0,0 +1,66 @@ +use core::{mem, ptr}; +use core::cell::RefCell; +use log::{self, Log, LogMetadata, LogRecord, LogLevelFilter}; +use log_buffer::LogBuffer; + +pub struct BufferLogger { + buffer: RefCell> +} + +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(&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: F) -> R { + f(unsafe { mem::transmute::<*const BufferLogger, &BufferLogger>(LOGGER) }) + } + + pub fn clear(&self) { + self.buffer.borrow_mut().clear() + } + + pub fn extract 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(), "{:>5}({}): {}", + record.level(), record.target(), record.args()).unwrap(); + println!("{:>5}({}): {}", + record.level(), record.target(), record.args()); + } + } +} diff --git a/artiq/runtime.rs/src/mailbox.rs b/artiq/runtime.rs/src/mailbox.rs new file mode 100644 index 000000000..030b7fb28 --- /dev/null +++ b/artiq/runtime.rs/src/mailbox.rs @@ -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) } +} diff --git a/artiq/runtime.rs/src/moninj.rs b/artiq/runtime.rs/src/moninj.rs new file mode 100644 index 000000000..13c8ef4cd --- /dev/null +++ b/artiq/runtime.rs/src/moninj.rs @@ -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 = [0; (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(); + } + } + } + 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) + } + } +} diff --git a/artiq/runtime.rs/src/moninj_proto.rs b/artiq/runtime.rs/src/moninj_proto.rs new file mode 100644 index 000000000..2dd613a6e --- /dev/null +++ b/artiq/runtime.rs/src/moninj_proto.rs @@ -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 { + 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 { + 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(()) + } +} diff --git a/artiq/runtime.rs/src/proto.rs b/artiq/runtime.rs/src/proto.rs new file mode 100644 index 000000000..9d59b6ad6 --- /dev/null +++ b/artiq/runtime.rs/src/proto.rs @@ -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 { + 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 { + 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 { + 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 { + 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> { + 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 { + 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()) +} diff --git a/artiq/runtime.rs/src/rpc_proto.rs b/artiq/runtime.rs/src/rpc_proto.rs new file mode 100644 index 000000000..d302d389d --- /dev/null +++ b/artiq/runtime.rs/src/rpc_proto.rs @@ -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> { + 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(()) + } + } +} diff --git a/artiq/runtime.rs/src/rpc_queue.rs b/artiq/runtime.rs/src/rpc_queue.rs new file mode 100644 index 000000000..3155813eb --- /dev/null +++ b/artiq/runtime.rs/src/rpc_queue.rs @@ -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(f: F) -> Result + where F: FnOnce(&mut [u8]) -> Result { + 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(f: F) -> Result + where F: FnOnce(&mut [u8]) -> Result { + 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) + }) + } +} diff --git a/artiq/runtime.rs/src/rtio_crg.rs b/artiq/runtime.rs/src/rtio_crg.rs new file mode 100644 index 000000000..416bab03f --- /dev/null +++ b/artiq/runtime.rs/src/rtio_crg.rs @@ -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}; diff --git a/artiq/runtime.rs/src/io.rs b/artiq/runtime.rs/src/sched.rs similarity index 57% rename from artiq/runtime.rs/src/io.rs rename to artiq/runtime.rs/src/sched.rs index 0607be728..4e698f94b 100644 --- a/artiq/runtime.rs/src/io.rs +++ b/artiq/runtime.rs/src/sched.rs @@ -1,16 +1,17 @@ -extern crate fringe; -extern crate lwip; +#![allow(dead_code)] use std::cell::RefCell; use std::vec::Vec; -use std::time::{Instant, Duration}; use std::io::{Read, Write, Result, Error, ErrorKind}; -use self::fringe::OwnedStack; -use self::fringe::generator::{Generator, Yielder}; +use fringe::OwnedStack; +use fringe::generator::{Generator, Yielder, State as GeneratorState}; +use lwip; +use clock; +use urc::Urc; #[derive(Debug)] struct WaitRequest { - timeout: Option, + timeout: Option, event: Option } @@ -23,48 +24,96 @@ enum WaitResult { #[derive(Debug)] struct Thread { - generator: Generator, + generator: Generator, waiting_for: WaitRequest, interrupted: bool } -#[derive(Debug)] -pub struct Scheduler { - threads: Vec, - index: usize -} - -impl Scheduler { - pub fn new() -> Scheduler { - Scheduler { threads: Vec::new(), index: 0 } - } - - pub unsafe fn spawn(&mut self, stack_size: usize, f: F) { +impl Thread { + unsafe fn new(spawner: Spawner, stack_size: usize, f: F) -> ThreadHandle + where F: 'static + FnOnce(Waiter, Spawner) + Send { let stack = OwnedStack::new(stack_size); - let thread = Thread { - generator: Generator::unsafe_new(stack, move |yielder, _| { - f(Waiter(yielder)) + ThreadHandle::new(Thread { + generator: Generator::unsafe_new(stack, |yielder, _| { + f(Waiter(yielder), spawner) }), waiting_for: WaitRequest { timeout: None, event: None }, interrupted: false - }; - self.threads.push(thread) + }) + } + + pub fn terminated(&self) -> bool { + // FIXME: https://github.com/nathan7/libfringe/pull/56 + match self.generator.state() { + GeneratorState::Unavailable => true, + GeneratorState::Runnable => false + } + } + + pub fn interrupt(&mut self) { + self.interrupted = true + } +} + +#[derive(Debug, Clone)] +pub struct ThreadHandle(Urc>); + +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, + index: usize, + spawner: Spawner +} + +impl Scheduler { + pub fn new() -> Scheduler { + Scheduler { + threads: Vec::new(), + index: 0, + spawner: Spawner::new() + } + } + + pub fn spawner(&self) -> &Spawner { + &self.spawner } pub fn run(&mut self) { + self.threads.append(&mut *self.spawner.queue.borrow_mut()); + if self.threads.len() == 0 { return } - let now = Instant::now(); + let now = clock::get_ms(); let start_index = self.index; loop { self.index = (self.index + 1) % self.threads.len(); let result = { - let thread = &mut self.threads[self.index]; + let thread = &mut *self.threads[self.index].0.borrow_mut(); match thread.waiting_for { _ if thread.interrupted => { thread.interrupted = false; @@ -95,7 +144,8 @@ impl Scheduler { }, Some(wait_request) => { // The thread has suspended itself. - self.threads[self.index].waiting_for = wait_request + let thread = &mut *self.threads[self.index].0.borrow_mut(); + thread.waiting_for = wait_request } } @@ -104,8 +154,27 @@ impl Scheduler { } } -#[derive(Debug)] +#[derive(Debug, Clone)] +pub struct Spawner { + queue: Urc>> +} + +impl Spawner { + fn new() -> Spawner { + Spawner { queue: Urc::new(RefCell::new(Vec::new())) } + } + + pub fn spawn(&self, stack_size: usize, f: F) -> ThreadHandle + where F: 'static + FnOnce(Waiter, Spawner) + Send { + let handle = unsafe { Thread::new(self.clone(), stack_size, f) }; + self.queue.borrow_mut().push(handle.clone()); + handle + } +} + enum WaitEvent { + Completion(*const (Fn() -> bool + 'static)), + Termination(*const RefCell), UdpReadable(*const RefCell), TcpAcceptable(*const RefCell), TcpWriteable(*const RefCell), @@ -115,6 +184,10 @@ enum WaitEvent { impl WaitEvent { fn completed(&self) -> bool { match *self { + WaitEvent::Completion(f) => + unsafe { (*f)() }, + WaitEvent::Termination(thread) => + unsafe { (*thread).borrow().terminated() }, WaitEvent::UdpReadable(state) => unsafe { (*state).borrow().readable() }, WaitEvent::TcpAcceptable(state) => @@ -127,15 +200,23 @@ impl WaitEvent { } } +// *const DST doesn't have impl Debug +impl ::core::fmt::Debug for WaitEvent { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> + ::core::result::Result<(), ::core::fmt::Error> { + write!(f, "WaitEvent...") + } +} + unsafe impl Send for WaitEvent {} #[derive(Debug, Clone, Copy)] pub struct Waiter<'a>(&'a Yielder); impl<'a> Waiter<'a> { - pub fn sleep(&self, duration: Duration) -> Result<()> { + pub fn sleep(&self, duration_ms: u64) -> Result<()> { let request = WaitRequest { - timeout: Some(Instant::now() + duration), + timeout: Some(clock::get_ms() + duration_ms), event: None }; @@ -154,6 +235,27 @@ impl<'a> Waiter<'a> { } } + pub fn relinquish(&self) -> Result<()> { + self.suspend(WaitRequest { + timeout: None, + event: None + }) + } + + pub fn join(&self, thread: ThreadHandle) -> Result<()> { + self.suspend(WaitRequest { + timeout: None, + event: Some(WaitEvent::Termination(&*thread.0)) + }) + } + + pub fn until bool + 'static>(&self, f: F) -> Result<()> { + self.suspend(WaitRequest { + timeout: None, + event: Some(WaitEvent::Completion(&f as *const _)) + }) + } + pub fn udp_readable(&self, socket: &lwip::UdpSocket) -> Result<()> { self.suspend(WaitRequest { timeout: None, @@ -185,7 +287,7 @@ impl<'a> Waiter<'a> { // Wrappers around lwip -pub use self::lwip::{IpAddr, IP4_ANY, IP6_ANY, IP_ANY, SocketAddr}; +pub use lwip::{IpAddr, IP4_ANY, IP6_ANY, IP_ANY, SocketAddr}; #[derive(Debug)] pub struct UdpSocket<'a> { @@ -201,6 +303,14 @@ impl<'a> UdpSocket<'a> { }) } + pub fn into_lower(self) -> lwip::UdpSocket { + self.lower + } + + pub fn from_lower(waiter: Waiter<'a>, inner: lwip::UdpSocket) -> UdpSocket { + UdpSocket { waiter: waiter, lower: inner } + } + pub fn bind(&self, addr: SocketAddr) -> Result<()> { Ok(try!(self.lower.bind(addr))) } @@ -218,12 +328,12 @@ impl<'a> UdpSocket<'a> { Ok(buf.len()) } - pub fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr)> { + pub fn recv_from(&self, buf: &mut Vec) -> Result { try!(self.waiter.udp_readable(&self.lower)); let (pbuf, addr) = self.lower.try_recv().unwrap(); - let len = ::std::cmp::min(buf.len(), pbuf.len()); - (&mut buf[..len]).copy_from_slice(&pbuf.as_slice()[..len]); - Ok((len, addr)) + buf.clear(); + buf.extend_from_slice(&pbuf.as_slice()); + Ok(addr) } pub fn send(&self, buf: &[u8]) -> Result { @@ -239,6 +349,10 @@ impl<'a> UdpSocket<'a> { (&mut buf[..len]).copy_from_slice(&pbuf.as_slice()[..len]); Ok(len) } + + pub fn readable(&self) -> bool { + self.lower.state().borrow().readable() + } } #[derive(Debug)] @@ -255,6 +369,14 @@ impl<'a> TcpListener<'a> { }) } + pub fn into_lower(self) -> lwip::TcpListener { + self.lower + } + + pub fn from_lower(waiter: Waiter<'a>, inner: lwip::TcpListener) -> TcpListener { + TcpListener { waiter: waiter, lower: inner } + } + pub fn accept(&self) -> Result<(TcpStream, SocketAddr)> { try!(self.waiter.tcp_acceptable(&self.lower)); let stream_lower = self.lower.try_accept().unwrap(); @@ -265,9 +387,23 @@ impl<'a> TcpListener<'a> { buffer: None }, addr)) } + + pub fn acceptable(&self) -> bool { + self.lower.state().borrow().acceptable() + } + + pub fn keepalive(&self) -> bool { + self.lower.keepalive() + } + + pub fn set_keepalive(&self, keepalive: bool) { + self.lower.set_keepalive(keepalive) + } } -pub use self::lwip::Shutdown; +pub use lwip::Shutdown; + +pub struct TcpStreamInner(lwip::TcpStream, Option<(lwip::Pbuf<'static>, usize)>); #[derive(Debug)] pub struct TcpStream<'a> { @@ -277,23 +413,43 @@ pub struct TcpStream<'a> { } impl<'a> TcpStream<'a> { + pub fn into_lower(self) -> TcpStreamInner { + TcpStreamInner(self.lower, self.buffer) + } + + pub fn from_lower(waiter: Waiter<'a>, inner: TcpStreamInner) -> TcpStream { + TcpStream { waiter: waiter, lower: inner.0, buffer: inner.1 } + } + pub fn shutdown(&self, how: Shutdown) -> Result<()> { Ok(try!(self.lower.shutdown(how))) } + + pub fn readable(&self) -> bool { + self.buffer.is_some() || self.lower.state().borrow().readable() + } + + pub fn writeable(&self) -> bool { + self.lower.state().borrow().writeable() + } } impl<'a> Read for TcpStream<'a> { fn read(&mut self, buf: &mut [u8]) -> Result { if self.buffer.is_none() { try!(self.waiter.tcp_readable(&self.lower)); - let pbuf = try!(self.lower.try_read()).unwrap(); - self.buffer = Some((pbuf, 0)) + match self.lower.try_read() { + Ok(Some(pbuf)) => self.buffer = Some((pbuf, 0)), + Ok(None) => unreachable!(), + Err(lwip::Error::ConnectionClosed) => return Ok(0), + Err(err) => return Err(Error::from(err)) + } } let (pbuf, pos) = self.buffer.take().unwrap(); let slice = &pbuf.as_slice()[pos..]; let len = ::std::cmp::min(buf.len(), slice.len()); - buf.copy_from_slice(&slice[..len]); + buf[..len].copy_from_slice(&slice[..len]); if len < slice.len() { self.buffer = Some((pbuf, pos + len)) } @@ -304,7 +460,9 @@ impl<'a> Read for TcpStream<'a> { impl<'a> Write for TcpStream<'a> { fn write(&mut self, buf: &[u8]) -> Result { 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<()> { diff --git a/artiq/runtime.rs/src/session.rs b/artiq/runtime.rs/src/session.rs new file mode 100644 index 000000000..8ad957a61 --- /dev/null +++ b/artiq/runtime.rs/src/session.rs @@ -0,0 +1,640 @@ +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 { + 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 TcpStream, 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(waiter: Waiter, f: F) -> io::Result + where F: FnOnce(&kern::Message) -> io::Result { + 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::(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(waiter: Waiter, f: F) -> io::Result + where F: FnOnce(&kern::Message) -> io::Result { + 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 { + 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) => { + try!(host_write(stream, host::Reply::RpcRequest { async: async })); + try!(rpc::send_args(&mut BufWriter::new(stream), 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(spawner: Spawner, waiter: Waiter, + handle: &mut Option, + 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(); + } +} diff --git a/artiq/runtime.rs/src/session_proto.rs b/artiq/runtime.rs/src/session_proto.rs new file mode 100644 index 000000000..fd48c46b3 --- /dev/null +++ b/artiq/runtime.rs/src/session_proto.rs @@ -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), + RunKernel, + + RpcReply { tag: Vec }, + RpcException { + name: String, + message: String, + param: [u64; 3], + file: String, + line: u32, + column: u32, + function: String, + }, + + FlashRead { key: String }, + FlashWrite { key: String, value: Vec }, + FlashRemove { key: String }, + FlashErase, +} + +impl Request { + pub fn read_from(reader: &mut Read) -> io::Result { + 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(()) + } +} diff --git a/artiq/runtime.rs/src/urc.rs b/artiq/runtime.rs/src/urc.rs new file mode 100644 index 000000000..cec7751ea --- /dev/null +++ b/artiq/runtime.rs/src/urc.rs @@ -0,0 +1,31 @@ +use std::rc::Rc; +use std::ops::Deref; +use std::fmt; + +pub struct Urc(Rc); + +impl Urc { + pub fn new(value: T) -> Urc { Urc(Rc::new(value)) } +} + +unsafe impl Send for Urc {} + +unsafe impl Sync for Urc {} + +impl Deref for Urc { + type Target = T; + + fn deref(&self) -> &Self::Target { self.0.deref() } +} + +impl Clone for Urc { + fn clone(&self) -> Urc { + Urc(self.0.clone()) + } +} + +impl fmt::Debug for Urc { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} diff --git a/artiq/runtime/Makefile b/artiq/runtime/Makefile index e0c276a12..e8936fdf4 100644 --- a/artiq/runtime/Makefile +++ b/artiq/runtime/Makefile @@ -3,23 +3,75 @@ include $(MISOC_DIRECTORY)/software/common.mak PYTHON ?= python3.5 -OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \ - session.o log.o analyzer.o moninj.o net_server.o bridge_ctl.o \ - ksupport_data.o kloader.o test_mode.o main.o -OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \ - bridge.o rtio.o dds.o i2c.o +OBJECTS := flash_storage.o main.o +OBJECTS_KSUPPORT := ksupport_glue.o artiq_personality.o rtio.o dds.o i2c.o -CFLAGS += -I$(LIBALLOC_DIRECTORY) \ +RUSTOUT_DIRECTORY := cargo/or1k-unknown-none/debug +CORE_IO_COMMIT := d40c593f42fafbac1ff3d827f6df96338b5b7d8b +export CORE_IO_COMMIT + +CFLAGS += \ + -I$(LIBALLOC_DIRECTORY) \ -I$(MISOC_DIRECTORY)/software/include/dyld \ -I$(LIBDYLD_DIRECTORY)/include \ -I$(LIBUNWIND_DIRECTORY) \ -I$(LIBUNWIND_DIRECTORY)/../unwinder/include \ -I$(LIBLWIP_DIRECTORY)/../lwip/src/include \ - -I$(LIBLWIP_DIRECTORY) \ - -DNDEBUG + -I$(LIBLWIP_DIRECTORY) +CFLAGS += -DNDEBUG + +LDFLAGS += --gc-sections \ + -L../libcompiler-rt \ + -L../libbase \ + -L../libm \ + -L../liballoc \ + -L../libunwind \ + -L../libdyld \ + -L../liblwip all: runtime.bin runtime.fbi +.PHONY: $(RUSTOUT_DIRECTORY)/libruntime.a +$(RUSTOUT_DIRECTORY)/libruntime.a: ksupport.elf + CARGO_TARGET_DIR=$(realpath .)/cargo \ + cargo rustc --verbose \ + --manifest-path $(realpath $(RUNTIME_DIRECTORY)/../runtime.rs/Cargo.toml) \ + --target=or1k-unknown-none -- \ + $(shell cat $(BUILDINC_DIRECTORY)/generated/rust-cfg) \ + -C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s \ + -L../libcompiler-rt + +runtime.elf: $(OBJECTS) $(RUSTOUT_DIRECTORY)/libruntime.a + $(LD) $(LDFLAGS) \ + -T $(RUNTIME_DIRECTORY)/runtime.ld \ + -o $@ \ + $^ \ + -lbase-nofloat -lcompiler-rt -lalloc -llwip + @chmod -x $@ + +.PHONY: $(RUSTOUT_DIRECTORY)/libksupport.a +$(RUSTOUT_DIRECTORY)/libksupport.a: + CARGO_TARGET_DIR=$(realpath .)/cargo \ + cargo rustc --verbose \ + --manifest-path $(realpath $(RUNTIME_DIRECTORY)/../runtime.rs/libksupport/Cargo.toml) \ + --target=or1k-unknown-none -- \ + $(shell cat $(BUILDINC_DIRECTORY)/generated/rust-cfg) \ + --cfg ksupport \ + -C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s \ + -L../libcompiler-rt + +ksupport.elf: $(OBJECTS_KSUPPORT) $(RUSTOUT_DIRECTORY)/libksupport.a + $(LD) $(LDFLAGS) \ + --eh-frame-hdr \ + -T $(RUNTIME_DIRECTORY)/ksupport.ld \ + -o $@ \ + $^ \ + -lbase -lm -lcompiler-rt -ldyld -lunwind + @chmod -x $@ + +%.o: $(RUNTIME_DIRECTORY)/%.c + $(compile) + %.bin: %.elf $(OBJCOPY) -O binary $< $@ @chmod -x $@ @@ -27,56 +79,10 @@ all: runtime.bin runtime.fbi %.fbi: %.bin @echo " MSCIMG " $@ && $(PYTHON) -m misoc.tools.mkmscimg -f -o $@ $< -runtime.elf: $(OBJECTS) libartiq_rust.a - $(LD) $(LDFLAGS) \ - --gc-sections \ - -T $(RUNTIME_DIRECTORY)/runtime.ld \ - -N -o $@ \ - ../libbase/crt0-$(CPU).o \ - $(OBJECTS) \ - -L../libcompiler-rt \ - -L../libbase \ - -L../libm \ - -L../liballoc \ - -L../liblwip \ - -Lcargo/or1k-unknown-none/debug/ \ - -lartiq_rust -lbase -lm -lcompiler-rt -lalloc -llwip - @chmod -x $@ - -ksupport.elf: $(OBJECTS_KSUPPORT) - $(LD) $(LDFLAGS) \ - --eh-frame-hdr \ - -T $(RUNTIME_DIRECTORY)/ksupport.ld \ - -N -o $@ \ - ../libbase/crt0-$(CPU).o \ - $^ \ - -L../libbase \ - -L../libcompiler-rt \ - -L../libunwind \ - -L../libdyld \ - -lbase-nofloat -lcompiler-rt -ldyld -lunwind - @chmod -x $@ - -ksupport_data.o: ksupport.elf - $(LD) -r -b binary -o $@ $< - -libartiq_rust.a: - CARGO_TARGET_DIR="./cargo" \ - cargo rustc --verbose \ - --manifest-path $(RUNTIME_DIRECTORY)/../runtime.rs/Cargo.toml \ - --target=or1k-unknown-none -- \ - -C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s \ - -L../libcompiler-rt - -%.o: $(RUNTIME_DIRECTORY)/%.c - $(compile) - -%.o: $(RUNTIME_DIRECTORY)/%.S - $(assemble) - clean: $(RM) $(OBJECTS) $(OBJECTS_KSUPPORT) $(RM) runtime.elf runtime.bin runtime.fbi .*~ *~ $(RM) ksupport.elf ksupport.bin + $(RM) -rf cargo .PHONY: all clean diff --git a/artiq/runtime/analyzer.c b/artiq/runtime/analyzer.c deleted file mode 100644 index 84e15b929..000000000 --- a/artiq/runtime/analyzer.c +++ /dev/null @@ -1,158 +0,0 @@ -#include -#include - -#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 diff --git a/artiq/runtime/analyzer.h b/artiq/runtime/analyzer.h deleted file mode 100644 index c94b664b2..000000000 --- a/artiq/runtime/analyzer.h +++ /dev/null @@ -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 */ diff --git a/artiq/runtime/artiq_personality.c b/artiq/runtime/artiq_personality.c index 3d52f3c30..2cc5704b4 100644 --- a/artiq/runtime/artiq_personality.c +++ b/artiq/runtime/artiq_personality.c @@ -233,7 +233,7 @@ struct artiq_raised_exception { struct _Unwind_Exception unwind; struct artiq_exception artiq; int handled; - struct artiq_backtrace_item backtrace[1024]; + uintptr_t backtrace[1024]; size_t backtrace_size; }; @@ -303,8 +303,7 @@ static _Unwind_Reason_Code __artiq_uncaught_exception( uintptr_t pcOffset = pc - funcStart; EH_LOG("===> uncaught (pc=%p+%p)", (void*)funcStart, (void*)pcOffset); - inflight->backtrace[inflight->backtrace_size].function = funcStart; - inflight->backtrace[inflight->backtrace_size].offset = pcOffset; + inflight->backtrace[inflight->backtrace_size] = funcStart + pcOffset; ++inflight->backtrace_size; if(actions & _UA_END_OF_STACK) { diff --git a/artiq/runtime/artiq_personality.h b/artiq/runtime/artiq_personality.h index 0ddf9e88f..b1ed1b8d9 100644 --- a/artiq/runtime/artiq_personality.h +++ b/artiq/runtime/artiq_personality.h @@ -17,11 +17,6 @@ struct artiq_exception { int64_t param[3]; }; -struct artiq_backtrace_item { - intptr_t function; - intptr_t offset; -}; - #ifdef __cplusplus extern "C" { #endif @@ -48,7 +43,7 @@ void __artiq_reraise(void) /* Called by the runtime */ void __artiq_terminate(struct artiq_exception *artiq_exn, - struct artiq_backtrace_item *backtrace, + uintptr_t *backtrace, size_t backtrace_size) __attribute__((noreturn)); diff --git a/artiq/runtime/bridge.c b/artiq/runtime/bridge.c deleted file mode 100644 index e5b40fefa..000000000 --- a/artiq/runtime/bridge.c +++ /dev/null @@ -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; - } - } -} diff --git a/artiq/runtime/bridge.h b/artiq/runtime/bridge.h deleted file mode 100644 index d9e4f3654..000000000 --- a/artiq/runtime/bridge.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __BRIDGE_H -#define __BRIDGE_H - -void bridge_main(void); - -#endif /* __BRIDGE_H */ diff --git a/artiq/runtime/bridge_ctl.c b/artiq/runtime/bridge_ctl.c deleted file mode 100644 index 1ebc4b0bc..000000000 --- a/artiq/runtime/bridge_ctl.c +++ /dev/null @@ -1,106 +0,0 @@ -#include - -#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); -} diff --git a/artiq/runtime/bridge_ctl.h b/artiq/runtime/bridge_ctl.h deleted file mode 100644 index 2929ee8f0..000000000 --- a/artiq/runtime/bridge_ctl.h +++ /dev/null @@ -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 */ diff --git a/artiq/runtime/clock.c b/artiq/runtime/clock.c deleted file mode 100644 index bbcf87d7c..000000000 --- a/artiq/runtime/clock.c +++ /dev/null @@ -1,89 +0,0 @@ -#include - -#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)) - return; - watchdogs[id].active = 0; -} - -int watchdog_expired(void) -{ - int i; - long long int t; - - t = 0x7fffffffffffffffLL; - for(i=0;i t; -} diff --git a/artiq/runtime/clock.h b/artiq/runtime/clock.h deleted file mode 100644 index 4f4895443..000000000 --- a/artiq/runtime/clock.h +++ /dev/null @@ -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 */ diff --git a/artiq/runtime/dds.c b/artiq/runtime/dds.c index 718d32bcd..5b221ecf9 100644 --- a/artiq/runtime/dds.c +++ b/artiq/runtime/dds.c @@ -6,7 +6,6 @@ #include "artiq_personality.h" #include "rtio.h" -#include "log.h" #include "dds.h" #define DURATION_WRITE (5 << CONFIG_RTIO_FINE_TS_WIDTH) diff --git a/artiq/runtime/flash_storage.c b/artiq/runtime/flash_storage.c index dfa74906e..3e18151d6 100644 --- a/artiq/runtime/flash_storage.c +++ b/artiq/runtime/flash_storage.c @@ -9,7 +9,6 @@ #include #include -#include "log.h" #include "flash_storage.h" #if (defined CSR_SPIFLASH_BASE && defined CONFIG_SPIFLASH_PAGE_SIZE) @@ -63,24 +62,24 @@ static int record_iter_next(struct iter_state *is, struct record *record, int *f return 0; if(record->size < 6) { - core_log("flash_storage might be corrupted: record size is %u (<6) at address %08x\n", - record->size, record->raw_record); + // core_log("flash_storage might be corrupted: record size is %u (<6) at address %08x\n", + // record->size, record->raw_record); if(fatal) *fatal = 1; return 0; } if(is->seek > is->buf_len - sizeof(record->size) - 2) { /* 2 is the minimum key length */ - core_log("flash_storage might be corrupted: END_MARKER missing at the end of " - "the storage sector\n"); + // core_log("flash_storage might be corrupted: END_MARKER missing at the end of " + // "the storage sector\n"); if(fatal) *fatal = 1; return 0; } if(record->size > is->buf_len - is->seek) { - core_log("flash_storage might be corrupted: invalid record_size %d at address %08x\n", - record->size, record->raw_record); + // core_log("flash_storage might be corrupted: invalid record_size %d at address %08x\n", + // record->size, record->raw_record); if(fatal) *fatal = 1; return 0; @@ -90,8 +89,8 @@ static int record_iter_next(struct iter_state *is, struct record *record, int *f record->key_len = strnlen(record->key, record->size - sizeof(record->size)) + 1; if(record->key_len == record->size - sizeof(record->size) + 1) { - core_log("flash_storage might be corrupted: invalid key length at address %08x\n", - record->raw_record); + // core_log("flash_storage might be corrupted: invalid key length at address %08x\n", + // record->raw_record); if(fatal) *fatal = 1; return 0; @@ -265,7 +264,7 @@ int fs_write(const char *key, const void *buffer, unsigned int buf_len) return 0; // Storage is definitely full. fatal_error: - core_log("fatal error: flash storage might be corrupted\n"); + // core_log("fatal error: flash storage might be corrupted\n"); return 0; } @@ -295,8 +294,8 @@ unsigned int fs_read(const char *key, void *buffer, unsigned int buf_len, unsign } } - if(fatal) - core_log("fatal error: flash storage might be corrupted\n"); + // if(fatal) + // core_log("fatal error: flash storage might be corrupted\n"); return read_length; } diff --git a/artiq/runtime/isr.c b/artiq/runtime/isr.c deleted file mode 100644 index f42fa0694..000000000 --- a/artiq/runtime/isr.c +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include -#include - -void isr(void); -void isr(void) -{ - unsigned int irqs; - - irqs = irq_pending() & irq_getmask(); - - if(irqs & (1 << UART_INTERRUPT)) - uart_isr(); -} diff --git a/artiq/runtime/kloader.c b/artiq/runtime/kloader.c deleted file mode 100644 index 19a4b4190..000000000 --- a/artiq/runtime/kloader.c +++ /dev/null @@ -1,206 +0,0 @@ -#include -#include - -#include - -#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; - } - } -} diff --git a/artiq/runtime/kloader.h b/artiq/runtime/kloader.h deleted file mode 100644 index feba89de9..000000000 --- a/artiq/runtime/kloader.h +++ /dev/null @@ -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 */ diff --git a/artiq/runtime/ksupport.c b/artiq/runtime/ksupport.c deleted file mode 100644 index 60a8eb023..000000000 --- a/artiq/runtime/ksupport.c +++ /dev/null @@ -1,610 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include -#include - -#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" - -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}, - - /* 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>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 = "", - .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 = "", - .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(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, 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)() = request->library_info->init; - 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); -} diff --git a/artiq/runtime/ksupport.h b/artiq/runtime/ksupport.h deleted file mode 100644 index fc3ef7662..000000000 --- a/artiq/runtime/ksupport.h +++ /dev/null @@ -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 */ diff --git a/artiq/runtime/ksupport.ld b/artiq/runtime/ksupport.ld index da231b69b..333db0739 100644 --- a/artiq/runtime/ksupport.ld +++ b/artiq/runtime/ksupport.ld @@ -1,20 +1,23 @@ INCLUDE generated/output_format.ld +STARTUP(crt0-or1k.o) ENTRY(_start) INCLUDE generated/regions.ld -/* First 32M of main memory are reserved for runtime +/* First 4M of main memory are reserved for runtime * code/data/heap, then comes kernel memory. - * First 128K of kernel memory are for support code. + * Next 4M of main memory are reserved for + * the background RPC queue. + * First 256K of kernel memory are for support code. + * Support code is loaded at ORIGIN-0x80 so that ELF headers + * are also loaded. */ MEMORY { - ksupport (RWX) : ORIGIN = 0x42000000, LENGTH = 0x20000 + ksupport (RWX) : ORIGIN = 0x40800000, LENGTH = 0x40000 } -/* On AMP systems, kernel stack is at the end of main RAM, - * before the runtime stack. Leave 1M for runtime stack. - */ -PROVIDE(_fstack = 0x40000000 + LENGTH(main_ram) - 1024*1024 - 4); +/* Kernel stack is at the end of main RAM. */ +PROVIDE(_fstack = ORIGIN(main_ram) + LENGTH(main_ram) - 4); /* Force ld to make the ELF header as loadable. */ PHDRS @@ -32,6 +35,16 @@ SECTIONS _etext = .; } :text + /* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */ + .got : { + _GLOBAL_OFFSET_TABLE_ = .; + *(.got) + } :text + + .got.plt : { + *(.got.plt) + } :text + .rodata : { . = ALIGN(4); @@ -43,12 +56,12 @@ SECTIONS .eh_frame : { - *(.eh_frame) + KEEP(*(.eh_frame)) } :text .eh_frame_hdr : { - *(.eh_frame_hdr) + KEEP(*(.eh_frame_hdr)) } :text :eh_frame .data : @@ -73,7 +86,10 @@ SECTIONS *(COMMON) . = ALIGN(4); _ebss = .; - . = ALIGN(8); - _heapstart = .; + } + + /DISCARD/ : + { + *(.debug*) } } diff --git a/artiq/runtime/ksupport_glue.c b/artiq/runtime/ksupport_glue.c new file mode 100644 index 000000000..515635c94 --- /dev/null +++ b/artiq/runtime/ksupport_glue.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include + +#include +#include + +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 = "", + .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 = "", + .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 */ +long lround(double x); +long lround(double x) +{ + return x < 0 ? floor(x) : ceil(x); +} + +/* 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; +} diff --git a/artiq/runtime/log.c b/artiq/runtime/log.c deleted file mode 100644 index 5779d8c94..000000000 --- a/artiq/runtime/log.c +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include -#include - -#include - -#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)); -} diff --git a/artiq/runtime/log.h b/artiq/runtime/log.h deleted file mode 100644 index fcb1fcf4e..000000000 --- a/artiq/runtime/log.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __LOG_H -#define __LOG_H - -#include - -#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 */ diff --git a/artiq/runtime/mailbox.c b/artiq/runtime/mailbox.c deleted file mode 100644 index 98e49045b..000000000 --- a/artiq/runtime/mailbox.c +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include -#include -#include -#include - -#include "mailbox.h" - -#define KERNELCPU_MAILBOX MMPTR(MAILBOX_BASE) - -static unsigned int last_transmission; - -static void _flush_cpu_dcache(void) -{ - unsigned long dccfgr; - unsigned long cache_set_size; - unsigned long cache_ways; - unsigned long cache_block_size; - unsigned long cache_size; - int i; - - dccfgr = mfspr(SPR_DCCFGR); - cache_ways = 1 << (dccfgr & SPR_ICCFGR_NCW); - cache_set_size = 1 << ((dccfgr & SPR_DCCFGR_NCS) >> 3); - cache_block_size = (dccfgr & SPR_DCCFGR_CBS) ? 32 : 16; - cache_size = cache_set_size * cache_ways * cache_block_size; - - for (i = 0; i < cache_size; i += cache_block_size) - mtspr(SPR_DCBIR, i); -} - -void mailbox_send(void *ptr) -{ - last_transmission = (unsigned int)ptr; - KERNELCPU_MAILBOX = last_transmission; -} - -int mailbox_acknowledged(void) -{ - unsigned int m; - - m = KERNELCPU_MAILBOX; - return !m || (m != last_transmission); -} - -void mailbox_send_and_wait(void *ptr) -{ - mailbox_send(ptr); - while(!mailbox_acknowledged()); -} - -void *mailbox_receive(void) -{ - unsigned int r; - - r = KERNELCPU_MAILBOX; - if(r == last_transmission) - return NULL; - else { - if(r) { - _flush_cpu_dcache(); - } - return (void *)r; - } -} - -void *mailbox_wait_and_receive(void) -{ - void *r; - - while(!(r = mailbox_receive())); - return r; -} - -void mailbox_acknowledge(void) -{ - KERNELCPU_MAILBOX = 0; -} diff --git a/artiq/runtime/mailbox.h b/artiq/runtime/mailbox.h deleted file mode 100644 index f1d40c973..000000000 --- a/artiq/runtime/mailbox.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __MAILBOX_H -#define __MAILBOX_H - -void mailbox_send(void *ptr); -int mailbox_acknowledged(void); -void mailbox_send_and_wait(void *ptr); - -void *mailbox_receive(void); -void *mailbox_wait_and_receive(void); -void mailbox_acknowledge(void); - -#endif /* __MAILBOX_H */ diff --git a/artiq/runtime/main.c b/artiq/runtime/main.c index 25fdc3b31..d5db5e5dc 100644 --- a/artiq/runtime/main.c +++ b/artiq/runtime/main.c @@ -24,26 +24,7 @@ #include #endif -#include "bridge_ctl.h" -#include "kloader.h" #include "flash_storage.h" -#include "clock.h" -#include "rtiocrg.h" -#include "test_mode.h" -#include "net_server.h" -#include "session.h" -#include "analyzer.h" -#include "moninj.h" - -u32_t sys_now(void) -{ - return clock_get_ms(); -} - -u32_t sys_jiffies(void) -{ - return clock_get_ms(); -} static struct netif netif; @@ -184,82 +165,6 @@ void network_init(void) #endif /* CSR_ETHMAC_BASE */ -static struct net_server_instance session_inst = { - .port = 1381, - .start = session_start, - .end = session_end, - .input = session_input, - .poll = session_poll, - .ack_consumed = session_ack_consumed, - .ack_sent = session_ack_sent -}; - -#ifdef CSR_RTIO_ANALYZER_BASE -static struct net_server_instance analyzer_inst = { - .port = 1382, - .start = analyzer_start, - .end = analyzer_end, - .input = analyzer_input, - .poll = analyzer_poll, - .ack_consumed = analyzer_ack_consumed, - .ack_sent = analyzer_ack_sent -}; -#endif - -static void regular_main(void) -{ - puts("Accepting network sessions."); - network_init(); - net_server_init(&session_inst); -#ifdef CSR_RTIO_ANALYZER_BASE - analyzer_init(); - net_server_init(&analyzer_inst); -#endif - moninj_init(); - - session_end(); - while(1) { - lwip_service(); - kloader_service_essential_kmsg(); - net_server_service(); - } -} - -static void blink_led(void) -{ - int i; - long long int t; - - for(i=0;i<3;i++) { -#ifdef CSR_LEDS_BASE - leds_out_write(1); -#endif - t = clock_get_ms(); - while(clock_get_ms() < t + 250); -#ifdef CSR_LEDS_BASE - leds_out_write(0); -#endif - t = clock_get_ms(); - while(clock_get_ms() < t + 250); - } -} - -static int check_test_mode(void) -{ - char c; - long long int t; - - t = clock_get_ms(); - while(clock_get_ms() < t + 1000) { - if(readchar_nonblock()) { - c = readchar(); - if((c == 't')||(c == 'T')) - return 1; - } - } - return 0; -} - extern void _fheap, _eheap; extern void rust_main(); @@ -268,28 +173,19 @@ u16_t tcp_sndbuf_(struct tcp_pcb *pcb) { return tcp_sndbuf(pcb); } +u8_t* tcp_so_options_(struct tcp_pcb *pcb) { + return &pcb->so_options; +} + int main(void) { irq_setmask(0); irq_setie(1); uart_init(); - puts("ARTIQ runtime built "__DATE__" "__TIME__"\n"); - alloc_give(&_fheap, &_eheap - &_fheap); - clock_init(); - rtiocrg_init(); - puts("Press 't' to enter test mode..."); - blink_led(); - if(check_test_mode()) { - puts("Entering test mode."); - test_main(); - } else { - puts("Entering regular mode."); - // rust_main(); - session_startup_kernel(); - regular_main(); - } + rust_main(); + return 0; } diff --git a/artiq/runtime/messages.h b/artiq/runtime/messages.h deleted file mode 100644 index 1a297d630..000000000 --- a/artiq/runtime/messages.h +++ /dev/null @@ -1,179 +0,0 @@ -#ifndef __MESSAGES_H -#define __MESSAGES_H - -#include -#include -#include - -enum { - MESSAGE_TYPE_LOAD_REPLY, - MESSAGE_TYPE_NOW_INIT_REQUEST, - MESSAGE_TYPE_NOW_INIT_REPLY, - MESSAGE_TYPE_NOW_SAVE, - MESSAGE_TYPE_FINISHED, - MESSAGE_TYPE_EXCEPTION, - MESSAGE_TYPE_WATCHDOG_SET_REQUEST, - MESSAGE_TYPE_WATCHDOG_SET_REPLY, - MESSAGE_TYPE_WATCHDOG_CLEAR, - MESSAGE_TYPE_RPC_SEND, - MESSAGE_TYPE_RPC_RECV_REQUEST, - MESSAGE_TYPE_RPC_RECV_REPLY, - MESSAGE_TYPE_RPC_BATCH, - MESSAGE_TYPE_CACHE_GET_REQUEST, - MESSAGE_TYPE_CACHE_GET_REPLY, - MESSAGE_TYPE_CACHE_PUT_REQUEST, - MESSAGE_TYPE_CACHE_PUT_REPLY, - MESSAGE_TYPE_LOG, - - MESSAGE_TYPE_BRG_READY, - MESSAGE_TYPE_BRG_TTL_O, - MESSAGE_TYPE_BRG_TTL_OE, - MESSAGE_TYPE_BRG_DDS_SEL, - MESSAGE_TYPE_BRG_DDS_RESET, - MESSAGE_TYPE_BRG_DDS_READ_REQUEST, - MESSAGE_TYPE_BRG_DDS_READ_REPLY, - MESSAGE_TYPE_BRG_DDS_WRITE, - MESSAGE_TYPE_BRG_DDS_FUD, -}; - -struct msg_base { - int type; -}; - -/* kernel messages */ - -struct msg_load_request { - const void *library; - struct dyld_info *library_info; - int run_kernel; -}; - -struct msg_load_reply { - int type; - const char *error; -}; - -struct msg_now_init_reply { - int type; - long long int now; -}; - -struct msg_now_save { - int type; - long long int now; -}; - -struct msg_exception { - int type; - struct artiq_exception *exception; - struct artiq_backtrace_item *backtrace; - size_t backtrace_size; -}; - -struct msg_watchdog_set_request { - int type; - int ms; -}; - -struct msg_watchdog_set_reply { - int type; - int id; -}; - -struct msg_watchdog_clear { - int type; - int id; -}; - -struct msg_rpc_send { - int type; - int service; - const char *tag; - va_list args; -}; - -struct msg_rpc_recv_request { - int type; - void *slot; -}; - -struct msg_rpc_recv_reply { - int type; - int alloc_size; - struct artiq_exception *exception; -}; - -struct msg_cache_get_request { - int type; - const char *key; -}; - -struct msg_cache_get_reply { - int type; - size_t length; - int32_t *elements; -}; - -struct msg_cache_put_request { - int type; - const char *key; - size_t length; - int32_t *elements; -}; - -struct msg_cache_put_reply { - int type; - int succeeded; -}; - -struct msg_log { - int type; - const char *fmt; - va_list args; -}; - -/* bridge messages */ - -struct msg_brg_ttl_out { - /* used for OE and O */ - int type; - int channel; - int value; -}; - -struct msg_brg_dds_sel { - int type; - int bus_channel; - int channel; -}; - -struct msg_brg_dds_reset { - int type; - int bus_channel; -}; - -struct msg_brg_dds_read_request { - int type; - int bus_channel; - unsigned int address; -}; - -struct msg_brg_dds_read_reply { - int type; - int bus_channel; - unsigned int data; -}; - -struct msg_brg_dds_write { - int type; - int bus_channel; - unsigned int address; - unsigned int data; -}; - -struct msg_brg_dds_fud { - int type; - int bus_channel; -}; - -#endif /* __MESSAGES_H */ diff --git a/artiq/runtime/moninj.c b/artiq/runtime/moninj.c deleted file mode 100644 index 67116626f..000000000 --- a/artiq/runtime/moninj.c +++ /dev/null @@ -1,166 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "log.h" -#include "moninj.h" - -enum { - MONINJ_REQ_MONITOR = 1, - MONINJ_REQ_TTLSET = 2 -}; - -enum { - MONINJ_TTL_MODE_EXP = 0, - MONINJ_TTL_MODE_1 = 1, - MONINJ_TTL_MODE_0 = 2, - MONINJ_TTL_MODE_IN = 3 -}; - -enum { - MONINJ_TTL_OVERRIDE_ENABLE = 0, - MONINJ_TTL_OVERRIDE_O = 1, - MONINJ_TTL_OVERRIDE_OE = 2 -}; - -static struct udp_pcb *listen_pcb; - -struct monitor_reply { - long long int ttl_levels; - long long int ttl_oes; - long long int ttl_overrides; - unsigned short int dds_rtio_first_channel; - unsigned short int dds_channels_per_bus; -#if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0)) - unsigned int dds_ftws[CONFIG_RTIO_DDS_COUNT*CONFIG_DDS_CHANNELS_PER_BUS]; -#endif -} __attribute__((packed)); - -static void moninj_monitor(const ip_addr_t *addr, u16_t port) -{ - struct monitor_reply reply; - int i; - struct pbuf *reply_p; - - reply.ttl_levels = 0; - reply.ttl_oes = 0; - reply.ttl_overrides = 0; - for(i=0;i 0)) - int j; - - reply.dds_rtio_first_channel = CONFIG_RTIO_FIRST_DDS_CHANNEL; - reply.dds_channels_per_bus = CONFIG_DDS_CHANNELS_PER_BUS; - for(j=0;jpayload, &reply, sizeof(struct monitor_reply)); - udp_sendto(listen_pcb, reply_p, addr, port); - pbuf_free(reply_p); -} - -static void moninj_ttlset(int channel, int mode) -{ - rtio_moninj_inj_chan_sel_write(channel); - switch(mode) { - case MONINJ_TTL_MODE_EXP: - rtio_moninj_inj_override_sel_write(MONINJ_TTL_OVERRIDE_ENABLE); - rtio_moninj_inj_value_write(0); - break; - case MONINJ_TTL_MODE_1: - rtio_moninj_inj_override_sel_write(MONINJ_TTL_OVERRIDE_O); - rtio_moninj_inj_value_write(1); - rtio_moninj_inj_override_sel_write(MONINJ_TTL_OVERRIDE_OE); - rtio_moninj_inj_value_write(1); - rtio_moninj_inj_override_sel_write(MONINJ_TTL_OVERRIDE_ENABLE); - rtio_moninj_inj_value_write(1); - break; - case MONINJ_TTL_MODE_0: - rtio_moninj_inj_override_sel_write(MONINJ_TTL_OVERRIDE_O); - rtio_moninj_inj_value_write(0); - rtio_moninj_inj_override_sel_write(MONINJ_TTL_OVERRIDE_OE); - rtio_moninj_inj_value_write(1); - rtio_moninj_inj_override_sel_write(MONINJ_TTL_OVERRIDE_ENABLE); - rtio_moninj_inj_value_write(1); - break; - case MONINJ_TTL_MODE_IN: - rtio_moninj_inj_override_sel_write(MONINJ_TTL_OVERRIDE_OE); - rtio_moninj_inj_value_write(0); - rtio_moninj_inj_override_sel_write(MONINJ_TTL_OVERRIDE_ENABLE); - rtio_moninj_inj_value_write(1); - break; - default: - core_log("unknown TTL mode %d\n", mode); - break; - } -} - -static void moninj_recv(void *arg, struct udp_pcb *upcb, struct pbuf *req, - const ip_addr_t *addr, u16_t port) -{ - char *p = (char *)req->payload; - - if(req->len >= 1) { - switch(p[0]) { - case MONINJ_REQ_MONITOR: - moninj_monitor(addr, port); - break; - case MONINJ_REQ_TTLSET: - if(req->len < 3) - break; - moninj_ttlset(p[1], p[2]); - break; - default: - break; - } - } - pbuf_free(req); /* beware: addr may point into the req pbuf */ -} - -void moninj_init(void) -{ - listen_pcb = udp_new(); - if(!listen_pcb) { - core_log("Failed to create UDP listening PCB\n"); - return; - } - udp_bind(listen_pcb, IP_ADDR_ANY, 3250); - udp_recv(listen_pcb, moninj_recv, NULL); -} diff --git a/artiq/runtime/moninj.h b/artiq/runtime/moninj.h deleted file mode 100644 index 1224b3b6e..000000000 --- a/artiq/runtime/moninj.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __MONINJ_H -#define __MONINJ_H - -void moninj_init(void); - -#endif /* __MONINJ_H */ diff --git a/artiq/runtime/net_server.c b/artiq/runtime/net_server.c deleted file mode 100644 index 4d09f0cf3..000000000 --- a/artiq/runtime/net_server.c +++ /dev/null @@ -1,227 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "net_server.h" - -struct net_server_connstate { - struct net_server_instance *instance; - int magic_recognized; - struct pbuf *rp; - int rp_offset; -}; - -static struct net_server_connstate *cs_new(struct net_server_instance *instance) -{ - struct net_server_connstate *cs; - - cs = (struct net_server_connstate *)mem_malloc(sizeof(struct net_server_connstate)); - if(!cs) - return NULL; - cs->instance = instance; - cs->magic_recognized = 0; - cs->rp = NULL; - cs->rp_offset = 0; - return cs; -} - -static void cs_free(struct net_server_connstate *cs) -{ - if(cs->rp) - pbuf_free(cs->rp); - mem_free(cs); -} - -static const char net_server_magic[] = "ARTIQ coredev\n"; - -static int magic_ok(struct net_server_connstate *cs) -{ - return cs->magic_recognized >= 14; -} - -static void net_server_close(struct net_server_connstate *cs, struct tcp_pcb *pcb) -{ - struct net_server_instance *instance; - - instance = cs->instance; - if(cs == instance->open_session_cs) { - instance->end(); - instance->open_session_cs = NULL; - instance->open_session_pcb = NULL; - } - - if(pcb) { - /* lwip loves to call back with broken pointers. Prevent that. */ - tcp_arg(pcb, NULL); - tcp_recv(pcb, NULL); - tcp_sent(pcb, NULL); - tcp_err(pcb, NULL); - - tcp_close(pcb); - } - cs_free(cs); -} - -static err_t net_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) -{ - struct net_server_connstate *cs; - - cs = (struct net_server_connstate *)arg; - if(p) { - if(cs->rp) - pbuf_cat(cs->rp, p); - else { - cs->rp = p; - cs->rp_offset = 0; - } - } else - net_server_close(cs, pcb); - return ERR_OK; -} - -static err_t net_server_sent(void *arg, struct tcp_pcb *pcb, u16_t len) -{ - struct net_server_connstate *cs; - - cs = (struct net_server_connstate *)arg; - cs->instance->ack_sent(len); - return ERR_OK; -} - -static void tcp_pcb_service(void *arg, struct tcp_pcb *pcb) -{ - struct net_server_connstate *cs; - struct net_server_instance *instance; - int remaining_in_pbuf; - char *rpp; - struct pbuf *next; - int r; - - cs = (struct net_server_connstate *)arg; - instance = cs->instance; - - /* Reader interface */ - while(cs->rp) { - remaining_in_pbuf = cs->rp->len - cs->rp_offset; - rpp = (char *)cs->rp->payload; - while(remaining_in_pbuf > 0) { - if(cs == instance->open_session_cs) { - r = instance->input(&rpp[cs->rp_offset], remaining_in_pbuf); - if(r > 0) { - tcp_recved(pcb, r); - cs->rp_offset += r; - remaining_in_pbuf -= r; - } else if(r == 0) - return; - else - net_server_close(cs, pcb); - } else { - if(rpp[cs->rp_offset] == net_server_magic[cs->magic_recognized]) { - cs->magic_recognized++; - if(magic_ok(cs)) { - if(instance->open_session_cs) - net_server_close(instance->open_session_cs, - instance->open_session_pcb); - instance->start(); - instance->open_session_cs = cs; - instance->open_session_pcb = pcb; - tcp_sent(pcb, net_server_sent); - } - } else { - net_server_close(cs, pcb); - return; - } - remaining_in_pbuf--; - cs->rp_offset++; - tcp_recved(pcb, 1); - } - } - next = cs->rp->next; - if(cs->rp->tot_len != cs->rp->len) { - pbuf_ref(next); - pbuf_free(cs->rp); - cs->rp = next; - cs->rp_offset = 0; - } else { - pbuf_free(cs->rp); - cs->rp = NULL; - } - } - - /* Writer interface */ - if(cs == instance->open_session_cs) { - void *data; - int len, sndbuf, close_flag; - - cs->instance->poll(&data, &len, &close_flag); - if(len > 0) { - sndbuf = tcp_sndbuf(pcb); - if(len > sndbuf) - len = sndbuf; - tcp_write(pcb, data, len, 0); - instance->ack_consumed(len); - } - if(close_flag) - tcp_output(pcb); - if((len < 0) || close_flag) - net_server_close(cs, pcb); - } -} - -static void net_server_err(void *arg, err_t err) -{ - struct net_server_connstate *cs; - - cs = (struct net_server_connstate *)arg; - net_server_close(cs, NULL); -} - -static err_t net_server_accept(void *arg, struct tcp_pcb *newpcb, err_t err) -{ - struct net_server_instance *instance; - struct net_server_connstate *cs; - - instance = (struct net_server_instance *)arg; - cs = cs_new(instance); - if(!cs) - return ERR_MEM; - tcp_accepted(instance->listen_pcb); - tcp_arg(newpcb, cs); - tcp_recv(newpcb, net_server_recv); - tcp_err(newpcb, net_server_err); - return ERR_OK; -} - -void net_server_init(struct net_server_instance *instance) -{ - struct tcp_pcb *bind_pcb; - - bind_pcb = tcp_new(); - bind_pcb->so_options |= SOF_KEEPALIVE; - tcp_bind(bind_pcb, IP_ADDR_ANY, instance->port); - - instance->listen_pcb = tcp_listen(bind_pcb); - tcp_arg(instance->listen_pcb, instance); - tcp_accept(instance->listen_pcb, net_server_accept); -} - -extern struct tcp_pcb *tcp_active_pcbs; - -void net_server_service(void) -{ - struct tcp_pcb *pcb; - - pcb = tcp_active_pcbs; - while(pcb) { - if(pcb->recv == net_server_recv) /* filter our connections */ - tcp_pcb_service(pcb->callback_arg, pcb); - pcb = pcb->next; - } -} diff --git a/artiq/runtime/net_server.h b/artiq/runtime/net_server.h deleted file mode 100644 index 9bb57d6c6..000000000 --- a/artiq/runtime/net_server.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef __NET_SERVER_H -#define __NET_SERVER_H - -struct net_server_connstate; -struct tcp_pcb; - -struct net_server_instance { - int port; - - void (*start)(void); - void (*end)(void); - int (*input)(void *data, int length); - void (*poll)(void **data, int *length, int *close_flag); - void (*ack_consumed)(int length); - void (*ack_sent)(int length); - - /* internal use */ - struct tcp_pcb *listen_pcb; - struct net_server_connstate *open_session_cs; - struct tcp_pcb *open_session_pcb; -}; - -void net_server_init(struct net_server_instance *instance); -void net_server_service(void); - -#endif /* __NET_SERVER_H */ diff --git a/artiq/runtime/rtiocrg.c b/artiq/runtime/rtiocrg.c deleted file mode 100644 index 2851cc994..000000000 --- a/artiq/runtime/rtiocrg.c +++ /dev/null @@ -1,71 +0,0 @@ -#include - -#include "log.h" -#include "clock.h" -#include "flash_storage.h" -#include "rtiocrg.h" - -void rtiocrg_init(void) -{ - char b; - int clk; - -#ifdef CSR_RTIO_CRG_PLL_RESET_ADDR - rtio_crg_pll_reset_write(0); -#endif - b = 'i'; - clk = 0; - fs_read("startup_clock", &b, 1, NULL); - if(b == 'i') - core_log("Startup RTIO clock: internal\n"); - else if(b == 'e') { - core_log("Startup RTIO clock: external\n"); - clk = 1; - } else - core_log("ERROR: unrecognized startup_clock entry in flash storage\n"); - - if(!rtiocrg_switch_clock(clk)) { - core_log("ERROR: startup RTIO clock failed\n"); - core_log("WARNING: this may cause the system initialization to fail\n"); - core_log("WARNING: fix clocking and reset the device\n"); - } -} - -int rtiocrg_check(void) -{ -#if ((defined CSR_RTIO_CRG_BASE) && (defined CSR_RTIO_CRG_PLL_RESET_ADDR)) - return rtio_crg_pll_locked_read(); -#else - return 1; -#endif -} - -int rtiocrg_switch_clock(int clk) -{ -#ifdef CSR_RTIO_CRG_BASE - int current_clk; - - current_clk = rtio_crg_clock_sel_read(); - if(clk == current_clk) { -#ifdef CSR_RTIO_CRG_PLL_RESET_ADDR - busywait_us(150); - if(!rtio_crg_pll_locked_read()) - return 0; -#endif - return 1; - } -#ifdef CSR_RTIO_CRG_PLL_RESET_ADDR - rtio_crg_pll_reset_write(1); -#endif - rtio_crg_clock_sel_write(clk); -#ifdef CSR_RTIO_CRG_PLL_RESET_ADDR - rtio_crg_pll_reset_write(0); - busywait_us(150); - if(!rtio_crg_pll_locked_read()) - return 0; -#endif - return 1; -#else /* CSR_RTIO_CRG_BASE */ - return 1; -#endif -} diff --git a/artiq/runtime/rtiocrg.h b/artiq/runtime/rtiocrg.h deleted file mode 100644 index df498f617..000000000 --- a/artiq/runtime/rtiocrg.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __RTIOCRG_H -#define __RTIOCRG_H - -void rtiocrg_init(void); -int rtiocrg_check(void); -int rtiocrg_switch_clock(int clk); - -#endif /* __RTIOCRG_H */ diff --git a/artiq/runtime/runtime.ld b/artiq/runtime/runtime.ld index b912c23c8..42e7e2cd2 100644 --- a/artiq/runtime/runtime.ld +++ b/artiq/runtime/runtime.ld @@ -1,4 +1,5 @@ INCLUDE generated/output_format.ld +STARTUP(crt0-or1k.o) ENTRY(_start) INCLUDE generated/regions.ld @@ -7,75 +8,79 @@ INCLUDE generated/regions.ld * ld does not allow this expression here. */ MEMORY { - runtime : ORIGIN = 0x40000000, LENGTH = 0x2000000 /* 32M */ + runtime (RWX) : ORIGIN = 0x40000000, LENGTH = 0x400000 /* 4M */ } -/* Kernel memory space start right after the runtime, - * and ends before the runtime stack. - * Runtime stack is always at the end of main_ram. - * This stack is shared with the kernel on UP systems. - */ -PROVIDE(_fstack = 0x40000000 + LENGTH(main_ram) - 4); - SECTIONS { - .text : - { - _ftext = .; - *(.text .stub .text.* .gnu.linkonce.t.*) - _etext = .; - } > runtime + .text : + { + _ftext = .; + *(.text .stub .text.* .gnu.linkonce.t.*) + _etext = .; + } > runtime - /* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */ - .got : { - _GLOBAL_OFFSET_TABLE_ = .; - *(.got) - } > runtime + /* https://sourceware.org/bugzilla/show_bug.cgi?id=20475 */ + .got : { + _GLOBAL_OFFSET_TABLE_ = .; + *(.got) + } > runtime - .got.plt : { - *(.got.plt) - } > runtime + .got.plt : { + *(.got.plt) + } > runtime - .rodata : - { - . = ALIGN(4); - _frodata = .; - *(.rodata .rodata.* .gnu.linkonce.r.*) - *(.rodata1) - _erodata = .; - } > runtime + .rodata : + { + . = ALIGN(4); + _frodata = .; + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.rodata1) + _erodata = .; + } > runtime - .data : - { - . = ALIGN(4); - _fdata = .; - *(.data .data.* .gnu.linkonce.d.*) - *(.data1) - *(.sdata .sdata.* .gnu.linkonce.s.*) - _edata = .; - } > runtime + .data : + { + . = ALIGN(4); + _fdata = .; + *(.data .data.* .gnu.linkonce.d.*) + *(.data1) + *(.sdata .sdata.* .gnu.linkonce.s.*) + _edata = .; + } > runtime - .bss : - { - . = ALIGN(4); - _fbss = .; - *(.dynsbss) - *(.sbss .sbss.* .gnu.linkonce.sb.*) - *(.scommon) - *(.dynbss) - *(.bss .bss.* .gnu.linkonce.b.*) - *(COMMON) - . = ALIGN(4); - _ebss = .; - . = ALIGN(8); - } > runtime + .bss : + { + . = ALIGN(4); + _fbss = .; + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + } > runtime - /DISCARD/ : - { - *(.eh_frame) - } + .stack : + { + . = ALIGN(0x1000); + _estack = .; + . += 0x4000; + _fstack = . - 4; + } > runtime - _fheap = .; - . += 0x1800000; - _eheap = .; + .heap : + { + _fheap = .; + . = ORIGIN(runtime) + LENGTH(runtime); + _eheap = .; + } > runtime + + /DISCARD/ : + { + *(.eh_frame) + *(.gcc_except_table) + } } diff --git a/artiq/runtime/session.c b/artiq/runtime/session.c deleted file mode 100644 index bbe89cb47..000000000 --- a/artiq/runtime/session.c +++ /dev/null @@ -1,1135 +0,0 @@ -#include -#include -#include -#include - -#include - -#include "mailbox.h" -#include "messages.h" - -#include "clock.h" -#include "log.h" -#include "kloader.h" -#include "artiq_personality.h" -#include "flash_storage.h" -#include "rtiocrg.h" -#include "session.h" - -// 2.5MiB in payload + 1KiB for headers. -// We need more than 1MiB to send a 1MiB list due to tags; -// about 5/4MiB for an 1MiB int32 list, 9/8MiB for an 1MiB int64 list. -#define BUFFER_SIZE (2560*1024 + 1024) -#define BUFFER_IN_SIZE BUFFER_SIZE -#define BUFFER_OUT_SIZE BUFFER_SIZE - -static int process_input(); -static int out_packet_available(); - -// ============================= Reader interface ============================= - -// Align the 9th byte (right after the header) of buffer_in so that -// the payload can be deserialized directly from the buffer using word reads. -static struct { - char padding[3]; - union { - char data[BUFFER_IN_SIZE]; - struct { - int32_t sync; - int32_t length; - int8_t type; - } __attribute__((packed)) header; - }; -} __attribute__((packed, aligned(4))) buffer_in; - -static int buffer_in_write_cursor, buffer_in_read_cursor; - -static void in_packet_reset() -{ - buffer_in_write_cursor = 0; - buffer_in_read_cursor = 0; -} - -static int in_packet_fill(uint8_t *data, int length) -{ - int consumed = 0; - while(consumed < length) { - /* Make sure the output buffer is available for any reply - * we might need to send. */ - if(!out_packet_available()) - break; - - if(buffer_in_write_cursor < 4) { - /* Haven't received the synchronization sequence yet. */ - buffer_in.data[buffer_in_write_cursor++] = data[consumed]; - - /* Framing error? */ - if(data[consumed++] != 0x5a) { - buffer_in_write_cursor = 0; - continue; - } - } else if(buffer_in_write_cursor < 8) { - /* Haven't received the packet length yet. */ - buffer_in.data[buffer_in_write_cursor++] = data[consumed++]; - } else if(buffer_in.header.length == 0) { - /* Zero-length packet means session reset. */ - return -2; - } else if(buffer_in.header.length > BUFFER_IN_SIZE) { - /* Packet wouldn't fit in the buffer. */ - return -1; - } else if(buffer_in.header.length > buffer_in_write_cursor) { - /* Receiving payload. */ - int remaining = buffer_in.header.length - buffer_in_write_cursor; - int amount = length - consumed > remaining ? remaining : length - consumed; - memcpy(&buffer_in.data[buffer_in_write_cursor], &data[consumed], - amount); - buffer_in_write_cursor += amount; - consumed += amount; - } - - if(buffer_in.header.length == buffer_in_write_cursor) { - /* We have a complete packet. */ - - buffer_in_read_cursor = sizeof(buffer_in.header); - if(!process_input()) - return -1; - - if(buffer_in_read_cursor < buffer_in_write_cursor) { - core_log("session.c: read underrun (%d bytes remaining)\n", - buffer_in_write_cursor - buffer_in_read_cursor); - } - - in_packet_reset(); - } - } - - return consumed; -} - -static void in_packet_chunk(void *ptr, int length) -{ - if(buffer_in_read_cursor + length > buffer_in_write_cursor) { - core_log("session.c: read overrun while trying to read %d bytes" - " (%d remaining)\n", - length, buffer_in_write_cursor - buffer_in_read_cursor); - } - - if(ptr != NULL) - memcpy(ptr, &buffer_in.data[buffer_in_read_cursor], length); - buffer_in_read_cursor += length; -} - -static int8_t in_packet_int8() -{ - int8_t result; - in_packet_chunk(&result, sizeof(result)); - return result; -} - -static int32_t in_packet_int32() -{ - int32_t result; - in_packet_chunk(&result, sizeof(result)); - return result; -} - -static int64_t in_packet_int64() -{ - int64_t result; - in_packet_chunk(&result, sizeof(result)); - return result; -} - -static const void *in_packet_bytes(int *length) -{ - *length = in_packet_int32(); - const void *ptr = &buffer_in.data[buffer_in_read_cursor]; - in_packet_chunk(NULL, *length); - return ptr; -} - -static const char *in_packet_string() -{ - int length; - const char *string = in_packet_bytes(&length); - if(string[length - 1] != 0) { - core_log("session.c: string is not zero-terminated\n"); - return ""; - } - return string; -} - -// ============================= Writer interface ============================= - -static union { - char data[BUFFER_OUT_SIZE]; - struct { - int32_t sync; - int32_t length; - int8_t type; - } __attribute__((packed)) header; -} buffer_out; - -static int buffer_out_read_cursor, buffer_out_sent_cursor, buffer_out_write_cursor; - -static void out_packet_reset() -{ - buffer_out_read_cursor = 0; - buffer_out_write_cursor = 0; - buffer_out_sent_cursor = 0; -} - -static int out_packet_available() -{ - return buffer_out_write_cursor == 0; -} - -static void out_packet_extract(void **data, int *length) -{ - if(buffer_out_write_cursor > 0 && - buffer_out.header.length > 0) { - *data = &buffer_out.data[buffer_out_read_cursor]; - *length = buffer_out_write_cursor - buffer_out_read_cursor; - } else { - *length = 0; - } -} - -static void out_packet_advance_consumed(int length) -{ - if(buffer_out_read_cursor + length > buffer_out_write_cursor) { - core_log("session.c: write underrun (consume) while trying to" - " acknowledge %d bytes (%d remaining)\n", - length, buffer_out_write_cursor - buffer_out_read_cursor); - return; - } - - buffer_out_read_cursor += length; -} - -static void out_packet_advance_sent(int length) -{ - if(buffer_out_sent_cursor + length > buffer_out_write_cursor) { - core_log("session.c: write underrun (send) while trying to" - " acknowledge %d bytes (%d remaining)\n", - length, buffer_out_write_cursor - buffer_out_sent_cursor); - return; - } - - buffer_out_sent_cursor += length; - if(buffer_out_sent_cursor == buffer_out_write_cursor) - out_packet_reset(); -} - -static int out_packet_chunk(const void *ptr, int length) -{ - if(buffer_out_write_cursor + length > BUFFER_OUT_SIZE) { - core_log("session.c: write overrun while trying to write %d bytes" - " (%d remaining)\n", - length, BUFFER_OUT_SIZE - buffer_out_write_cursor); - return 0; - } - - memcpy(&buffer_out.data[buffer_out_write_cursor], ptr, length); - buffer_out_write_cursor += length; - return 1; -} - -static void out_packet_start(int type) -{ - buffer_out.header.sync = 0x5a5a5a5a; - buffer_out.header.type = type; - buffer_out.header.length = 0; - buffer_out_write_cursor = sizeof(buffer_out.header); -} - -static void out_packet_finish() -{ - buffer_out.header.length = buffer_out_write_cursor; -} - -static void out_packet_empty(int type) -{ - out_packet_start(type); - out_packet_finish(); -} - -static int out_packet_int8(int8_t value) -{ - return out_packet_chunk(&value, sizeof(value)); -} - -static int out_packet_int32(int32_t value) -{ - return out_packet_chunk(&value, sizeof(value)); -} - -static int out_packet_int64(int64_t value) -{ - return out_packet_chunk(&value, sizeof(value)); -} - -static int out_packet_float64(double value) -{ - return out_packet_chunk(&value, sizeof(value)); -} - -static int out_packet_bytes(const void *ptr, int length) -{ - return out_packet_int32(length) && - out_packet_chunk(ptr, length); -} - -static int out_packet_string(const char *string) -{ - return out_packet_bytes(string, strlen(string) + 1); -} - -// =============================== API handling =============================== - -static int user_kernel_state; - -enum { - USER_KERNEL_NONE = 0, - USER_KERNEL_LOADED, - USER_KERNEL_RUNNING, - USER_KERNEL_WAIT_RPC /* < must come after _RUNNING */ -}; - -void session_startup_kernel(void) -{ - struct msg_base *umsg; - - watchdog_init(); - if(!kloader_start_startup_kernel()) - return; - - core_log("Startup kernel started\n"); - while(1) { - kloader_service_essential_kmsg(); - - umsg = mailbox_receive(); - if(umsg) { - if(!kloader_validate_kpointer(umsg)) - break; - if(kloader_is_essential_kmsg(umsg->type)) - continue; - if(umsg->type == MESSAGE_TYPE_FINISHED) - break; - else if(umsg->type == MESSAGE_TYPE_EXCEPTION) { - core_log("WARNING: startup kernel ended with exception\n"); - break; - } else { - core_log("ERROR: received invalid message type from kernel CPU\n"); - break; - } - } - - if(watchdog_expired()) { - core_log("WARNING: watchdog expired in startup kernel\n"); - break; - } - } - kloader_stop(); - core_log("Startup kernel terminated\n"); -} - -void session_start(void) -{ - in_packet_reset(); - out_packet_reset(); - - kloader_stop(); - user_kernel_state = USER_KERNEL_NONE; -} - -void session_end(void) -{ - kloader_stop(); - watchdog_init(); - kloader_start_idle_kernel(); -} - -/* host to device */ -enum { - REMOTEMSG_TYPE_LOG_REQUEST = 1, - REMOTEMSG_TYPE_LOG_CLEAR, - - REMOTEMSG_TYPE_IDENT_REQUEST, - REMOTEMSG_TYPE_SWITCH_CLOCK, - - REMOTEMSG_TYPE_LOAD_LIBRARY, - REMOTEMSG_TYPE_RUN_KERNEL, - - REMOTEMSG_TYPE_RPC_REPLY, - REMOTEMSG_TYPE_RPC_EXCEPTION, - - REMOTEMSG_TYPE_FLASH_READ_REQUEST, - REMOTEMSG_TYPE_FLASH_WRITE_REQUEST, - REMOTEMSG_TYPE_FLASH_ERASE_REQUEST, - REMOTEMSG_TYPE_FLASH_REMOVE_REQUEST -}; - -/* device to host */ -enum { - REMOTEMSG_TYPE_LOG_REPLY = 1, - - REMOTEMSG_TYPE_IDENT_REPLY, - REMOTEMSG_TYPE_CLOCK_SWITCH_COMPLETED, - REMOTEMSG_TYPE_CLOCK_SWITCH_FAILED, - - REMOTEMSG_TYPE_LOAD_COMPLETED, - REMOTEMSG_TYPE_LOAD_FAILED, - - REMOTEMSG_TYPE_KERNEL_FINISHED, - REMOTEMSG_TYPE_KERNEL_STARTUP_FAILED, - REMOTEMSG_TYPE_KERNEL_EXCEPTION, - - REMOTEMSG_TYPE_RPC_REQUEST, - - REMOTEMSG_TYPE_FLASH_READ_REPLY, - REMOTEMSG_TYPE_FLASH_OK_REPLY, - REMOTEMSG_TYPE_FLASH_ERROR_REPLY, - - REMOTEMSG_TYPE_WATCHDOG_EXPIRED, - REMOTEMSG_TYPE_CLOCK_FAILURE, -}; - -static int receive_rpc_value(const char **tag, void **slot); - -static int process_input(void) -{ - switch(buffer_in.header.type) { - case REMOTEMSG_TYPE_IDENT_REQUEST: { - char version[IDENT_SIZE]; - - get_ident(version); - - out_packet_start(REMOTEMSG_TYPE_IDENT_REPLY); - out_packet_chunk("AROR", 4); - out_packet_chunk(version, strlen(version)); - out_packet_finish(); - break; - } - - case REMOTEMSG_TYPE_SWITCH_CLOCK: { - int clk = in_packet_int8(); - - if(user_kernel_state >= USER_KERNEL_RUNNING) { - core_log("Attempted to switch RTIO clock while kernel running\n"); - out_packet_empty(REMOTEMSG_TYPE_CLOCK_SWITCH_FAILED); - break; - } - - if(rtiocrg_switch_clock(clk)) - out_packet_empty(REMOTEMSG_TYPE_CLOCK_SWITCH_COMPLETED); - else - out_packet_empty(REMOTEMSG_TYPE_CLOCK_SWITCH_FAILED); - break; - } - - case REMOTEMSG_TYPE_LOG_REQUEST: -#if (LOG_BUFFER_SIZE + 9) > BUFFER_OUT_SIZE -#error Output buffer cannot hold the log buffer -#endif - out_packet_start(REMOTEMSG_TYPE_LOG_REPLY); - core_log_get(&buffer_out.data[buffer_out_write_cursor]); - buffer_out_write_cursor += LOG_BUFFER_SIZE; - out_packet_finish(); - break; - - case REMOTEMSG_TYPE_LOG_CLEAR: - core_log_clear(); - out_packet_empty(REMOTEMSG_TYPE_LOG_REPLY); - break; - - case REMOTEMSG_TYPE_FLASH_READ_REQUEST: { -#if CONFIG_SPIFLASH_SECTOR_SIZE - 4 > BUFFER_OUT_SIZE - 9 -#error Output buffer cannot hold the flash storage data -#endif - const char *key = in_packet_string(); - int value_length; - - out_packet_start(REMOTEMSG_TYPE_FLASH_READ_REPLY); - value_length = fs_read(key, &buffer_out.data[buffer_out_write_cursor], - sizeof(buffer_out.data) - buffer_out_write_cursor, NULL); - buffer_out_write_cursor += value_length; - out_packet_finish(); - break; - } - - case REMOTEMSG_TYPE_FLASH_WRITE_REQUEST: { -#if CONFIG_SPIFLASH_SECTOR_SIZE - 4 > BUFFER_IN_SIZE - 9 -#error Input buffer cannot hold the flash storage data -#endif - const char *key, *value; - int value_length; - key = in_packet_string(); - value = in_packet_bytes(&value_length); - - if(fs_write(key, value, value_length)) - out_packet_empty(REMOTEMSG_TYPE_FLASH_OK_REPLY); - else - out_packet_empty(REMOTEMSG_TYPE_FLASH_ERROR_REPLY); - break; - } - - case REMOTEMSG_TYPE_FLASH_ERASE_REQUEST: - fs_erase(); - out_packet_empty(REMOTEMSG_TYPE_FLASH_OK_REPLY); - break; - - case REMOTEMSG_TYPE_FLASH_REMOVE_REQUEST: { - const char *key = in_packet_string(); - - fs_remove(key); - out_packet_empty(REMOTEMSG_TYPE_FLASH_OK_REPLY); - break; - } - - case REMOTEMSG_TYPE_LOAD_LIBRARY: { - const void *kernel = &buffer_in.data[buffer_in_read_cursor]; - buffer_in_read_cursor = buffer_in_write_cursor; - - if(user_kernel_state >= USER_KERNEL_RUNNING) { - core_log("Attempted to load new kernel library while already running\n"); - out_packet_empty(REMOTEMSG_TYPE_LOAD_FAILED); - break; - } - - if(kloader_load_library(kernel)) { - out_packet_empty(REMOTEMSG_TYPE_LOAD_COMPLETED); - user_kernel_state = USER_KERNEL_LOADED; - } else { - out_packet_empty(REMOTEMSG_TYPE_LOAD_FAILED); - } - break; - } - - case REMOTEMSG_TYPE_RUN_KERNEL: - if(user_kernel_state != USER_KERNEL_LOADED) { - core_log("Attempted to run kernel while not in the LOADED state\n"); - out_packet_empty(REMOTEMSG_TYPE_KERNEL_STARTUP_FAILED); - break; - } - - watchdog_init(); - kloader_start_kernel(); - - user_kernel_state = USER_KERNEL_RUNNING; - break; - - case REMOTEMSG_TYPE_RPC_REPLY: { - struct msg_rpc_recv_request *request; - struct msg_rpc_recv_reply reply; - - if(user_kernel_state != USER_KERNEL_WAIT_RPC) { - core_log("Unsolicited RPC reply\n"); - return 0; // restart session - } - - request = mailbox_wait_and_receive(); - if(request->type != MESSAGE_TYPE_RPC_RECV_REQUEST) { - core_log("Expected MESSAGE_TYPE_RPC_RECV_REQUEST, got %d\n", - request->type); - return 0; // restart session - } - - const char *tag = in_packet_string(); - void *slot = request->slot; - if(!receive_rpc_value(&tag, &slot)) { - core_log("Failed to receive RPC reply\n"); - return 0; // restart session - } - - reply.type = MESSAGE_TYPE_RPC_RECV_REPLY; - reply.alloc_size = 0; - reply.exception = NULL; - mailbox_send_and_wait(&reply); - - user_kernel_state = USER_KERNEL_RUNNING; - break; - } - - case REMOTEMSG_TYPE_RPC_EXCEPTION: { - struct msg_rpc_recv_request *request; - struct msg_rpc_recv_reply reply; - - struct artiq_exception exception; - exception.name = in_packet_string(); - exception.message = in_packet_string(); - exception.param[0] = in_packet_int64(); - exception.param[1] = in_packet_int64(); - exception.param[2] = in_packet_int64(); - exception.file = in_packet_string(); - exception.line = in_packet_int32(); - exception.column = in_packet_int32(); - exception.function = in_packet_string(); - - if(user_kernel_state != USER_KERNEL_WAIT_RPC) { - core_log("Unsolicited RPC exception reply\n"); - return 0; // restart session - } - - request = mailbox_wait_and_receive(); - if(request->type != MESSAGE_TYPE_RPC_RECV_REQUEST) { - core_log("Expected MESSAGE_TYPE_RPC_RECV_REQUEST, got %d\n", - request->type); - return 0; // restart session - } - - reply.type = MESSAGE_TYPE_RPC_RECV_REPLY; - reply.alloc_size = 0; - reply.exception = &exception; - mailbox_send_and_wait(&reply); - - user_kernel_state = USER_KERNEL_RUNNING; - break; - } - - default: - core_log("Received invalid packet type %d from host\n", - buffer_in.header.type); - return 0; - } - - return 1; -} - -// See comm_generic.py:_{send,receive}_rpc_value and llvm_ir_generator.py:_rpc_tag. -static void skip_rpc_value(const char **tag) { - switch(*(*tag)++) { - case 't': { - int size = *(*tag)++; - for(int i = 0; i < size; i++) - skip_rpc_value(tag); - break; - } - - case 'l': - case 'a': - skip_rpc_value(tag); - break; - - case 'r': - skip_rpc_value(tag); - break; - } -} - -static int sizeof_rpc_value(const char **tag) -{ - switch(*(*tag)++) { - case 't': { // tuple - int size = *(*tag)++; - - int32_t length = 0; - for(int i = 0; i < size; i++) - length += sizeof_rpc_value(tag); - return length; - } - - case 'n': // None - return 0; - - case 'b': // bool - return sizeof(int8_t); - - case 'i': // int(width=32) - return sizeof(int32_t); - - case 'I': // int(width=64) - return sizeof(int64_t); - - case 'f': // float - return sizeof(double); - - case 'F': // Fraction - return sizeof(struct { int64_t numerator, denominator; }); - - case 's': // string - return sizeof(char *); - - case 'l': // list(elt='a) - case 'a': // array(elt='a) - skip_rpc_value(tag); - return sizeof(struct { int32_t length; struct {} *elements; }); - - case 'r': // range(elt='a) - return sizeof_rpc_value(tag) * 3; - - default: - core_log("sizeof_rpc_value: unknown tag %02x\n", *((*tag) - 1)); - return 0; - } -} - -static void *alloc_rpc_value(int size) -{ - struct msg_rpc_recv_request *request; - struct msg_rpc_recv_reply reply; - - reply.type = MESSAGE_TYPE_RPC_RECV_REPLY; - reply.alloc_size = size; - reply.exception = NULL; - mailbox_send_and_wait(&reply); - - request = mailbox_wait_and_receive(); - if(request->type != MESSAGE_TYPE_RPC_RECV_REQUEST) { - core_log("Expected MESSAGE_TYPE_RPC_RECV_REQUEST, got %d\n", - request->type); - return NULL; - } - return request->slot; -} - -static int receive_rpc_value(const char **tag, void **slot) -{ - switch(*(*tag)++) { - case 't': { // tuple - int size = *(*tag)++; - - for(int i = 0; i < size; i++) { - if(!receive_rpc_value(tag, slot)) - return 0; - } - break; - } - - case 'n': // None - break; - - case 'b': { // bool - *((*(int8_t**)slot)++) = in_packet_int8(); - break; - } - - case 'i': { // int(width=32) - *((*(int32_t**)slot)++) = in_packet_int32(); - break; - } - - case 'I': { // int(width=64) - *((*(int64_t**)slot)++) = in_packet_int64(); - break; - } - - case 'f': { // float - *((*(int64_t**)slot)++) = in_packet_int64(); - break; - } - - case 'F': { // Fraction - struct { int64_t numerator, denominator; } *fraction = *slot; - fraction->numerator = in_packet_int64(); - fraction->denominator = in_packet_int64(); - *slot = (void*)((intptr_t)(*slot) + sizeof(*fraction)); - break; - } - - case 's': { // string - const char *in_string = in_packet_string(); - char *out_string = alloc_rpc_value(strlen(in_string) + 1); - memcpy(out_string, in_string, strlen(in_string) + 1); - *((*(char***)slot)++) = out_string; - break; - } - - case 'l': // list(elt='a) - case 'a': { // array(elt='a) - struct { int32_t length; struct {} *elements; } *list = *slot; - list->length = in_packet_int32(); - - const char *tag_copy = *tag; - list->elements = alloc_rpc_value(sizeof_rpc_value(&tag_copy) * list->length); - - void *element = list->elements; - for(int i = 0; i < list->length; i++) { - const char *tag_copy = *tag; - if(!receive_rpc_value(&tag_copy, &element)) - return 0; - } - skip_rpc_value(tag); - break; - } - - case 'r': { // range(elt='a) - const char *tag_copy; - tag_copy = *tag; - if(!receive_rpc_value(&tag_copy, slot)) // min - return 0; - tag_copy = *tag; - if(!receive_rpc_value(&tag_copy, slot)) // max - return 0; - tag_copy = *tag; - if(!receive_rpc_value(&tag_copy, slot)) // step - return 0; - *tag = tag_copy; - break; - } - - default: - core_log("receive_rpc_value: unknown tag %02x\n", *((*tag) - 1)); - return 0; - } - - return 1; -} - -static int send_rpc_value(const char **tag, void **value) -{ - if(!out_packet_int8(**tag)) - return 0; - - switch(*(*tag)++) { - case 't': { // tuple - int size = *(*tag)++; - if(!out_packet_int8(size)) - return 0; - - for(int i = 0; i < size; i++) { - if(!send_rpc_value(tag, value)) - return 0; - } - break; - } - - case 'n': // None - break; - - case 'b': { // bool - return out_packet_int8(*((*(int8_t**)value)++)); - } - - case 'i': { // int(width=32) - return out_packet_int32(*((*(int32_t**)value)++)); - } - - case 'I': { // int(width=64) - return out_packet_int64(*((*(int64_t**)value)++)); - } - - case 'f': { // float - return out_packet_float64(*((*(double**)value)++)); - } - - case 'F': { // Fraction - struct { int64_t numerator, denominator; } *fraction = *value; - if(!out_packet_int64(fraction->numerator)) - return 0; - if(!out_packet_int64(fraction->denominator)) - return 0; - *value = (void*)((intptr_t)(*value) + sizeof(*fraction)); - break; - } - - case 's': { // string - return out_packet_string(*((*(const char***)value)++)); - } - - case 'l': // list(elt='a) - case 'a': { // array(elt='a) - struct { uint32_t length; struct {} *elements; } *list = *value; - void *element = list->elements; - - if(!out_packet_int32(list->length)) - return 0; - - for(int i = 0; i < list->length; i++) { - const char *tag_copy = *tag; - if(!send_rpc_value(&tag_copy, &element)) { - core_log("failed to send list at element %d/%d\n", i, list->length); - return 0; - } - } - skip_rpc_value(tag); - - *value = (void*)((intptr_t)(*value) + sizeof(*list)); - break; - } - - case 'r': { // range(elt='a) - const char *tag_copy; - tag_copy = *tag; - if(!send_rpc_value(&tag_copy, value)) // min - return 0; - tag_copy = *tag; - if(!send_rpc_value(&tag_copy, value)) // max - return 0; - tag_copy = *tag; - if(!send_rpc_value(&tag_copy, value)) // step - return 0; - *tag = tag_copy; - break; - } - - case 'k': { // keyword(value='a) - struct { const char *name; struct {} contents; } *option = *value; - void *contents = &option->contents; - - if(!out_packet_string(option->name)) - return 0; - - // keyword never appears in composite types, so we don't have - // to accurately advance *value. - return send_rpc_value(tag, &contents); - } - - case 'O': { // host object - struct { uint32_t id; } **object = *value; - - if(!out_packet_int32((*object)->id)) - return 0; - - *value = (void*)((intptr_t)(*value) + sizeof(*object)); - break; - } - - default: - core_log("send_rpc_value: unknown tag %02x\n", *((*tag) - 1)); - return 0; - } - - return 1; -} - -static int send_rpc_request(int service, const char *tag, va_list args) -{ - out_packet_start(REMOTEMSG_TYPE_RPC_REQUEST); - out_packet_int32(service); - - while(*tag != ':') { - void *value = va_arg(args, void*); - if(!kloader_validate_kpointer(value)) - return 0; - if(!send_rpc_value(&tag, &value)) - return 0; - } - out_packet_int8(0); - - out_packet_string(tag + 1); // return tags - out_packet_finish(); - return 1; -} - -struct cache_row { - struct cache_row *next; - char *key; - size_t length; - int32_t *elements; - int borrowed; -}; - -static struct cache_row *cache; - -/* assumes output buffer is empty when called */ -static int process_kmsg(struct msg_base *umsg) -{ - if(!kloader_validate_kpointer(umsg)) - return 0; - if(kloader_is_essential_kmsg(umsg->type)) - return 1; /* handled elsewhere */ - if(user_kernel_state == USER_KERNEL_WAIT_RPC && - umsg->type == MESSAGE_TYPE_RPC_RECV_REQUEST) { - // Handled and acknowledged when we receive - // REMOTEMSG_TYPE_RPC_{EXCEPTION,REPLY}. - return 1; - } - if(user_kernel_state != USER_KERNEL_RUNNING) { - core_log("Received unexpected message from kernel CPU while not in running state\n"); - return 0; - } - - switch(umsg->type) { - case MESSAGE_TYPE_FINISHED: - out_packet_empty(REMOTEMSG_TYPE_KERNEL_FINISHED); - - for(struct cache_row *iter = cache; iter; iter = iter->next) - iter->borrowed = 0; - - kloader_stop(); - user_kernel_state = USER_KERNEL_LOADED; - - break; - - case MESSAGE_TYPE_EXCEPTION: { - struct msg_exception *msg = (struct msg_exception *)umsg; - - out_packet_start(REMOTEMSG_TYPE_KERNEL_EXCEPTION); - - out_packet_string(msg->exception->name); - out_packet_string(msg->exception->message); - out_packet_int64(msg->exception->param[0]); - out_packet_int64(msg->exception->param[1]); - out_packet_int64(msg->exception->param[2]); - - out_packet_string(msg->exception->file); - out_packet_int32(msg->exception->line); - out_packet_int32(msg->exception->column); - out_packet_string(msg->exception->function); - - kloader_filter_backtrace(msg->backtrace, - &msg->backtrace_size); - - out_packet_int32(msg->backtrace_size); - for(int i = 0; i < msg->backtrace_size; i++) { - struct artiq_backtrace_item *item = &msg->backtrace[i]; - out_packet_int32(item->function + item->offset); - } - - out_packet_finish(); - - kloader_stop(); - user_kernel_state = USER_KERNEL_LOADED; - mailbox_acknowledge(); - break; - } - - case MESSAGE_TYPE_RPC_SEND: - case MESSAGE_TYPE_RPC_BATCH: { - struct msg_rpc_send *msg = (struct msg_rpc_send *)umsg; - - if(!send_rpc_request(msg->service, msg->tag, msg->args)) { - core_log("Failed to send RPC request (service %d, tag %s)\n", - msg->service, msg->tag); - return 0; // restart session - } - - if(msg->type == MESSAGE_TYPE_RPC_SEND) - user_kernel_state = USER_KERNEL_WAIT_RPC; - mailbox_acknowledge(); - break; - } - - case MESSAGE_TYPE_CACHE_GET_REQUEST: { - struct msg_cache_get_request *request = (struct msg_cache_get_request *)umsg; - struct msg_cache_get_reply reply; - - reply.type = MESSAGE_TYPE_CACHE_GET_REPLY; - reply.length = 0; - reply.elements = NULL; - - for(struct cache_row *iter = cache; iter; iter = iter->next) { - if(!strcmp(iter->key, request->key)) { - reply.length = iter->length; - reply.elements = iter->elements; - iter->borrowed = 1; - break; - } - } - - mailbox_send(&reply); - break; - } - - case MESSAGE_TYPE_CACHE_PUT_REQUEST: { - struct msg_cache_put_request *request = (struct msg_cache_put_request *)umsg; - struct msg_cache_put_reply reply; - - reply.type = MESSAGE_TYPE_CACHE_PUT_REPLY; - - struct cache_row *row = NULL; - for(struct cache_row *iter = cache; iter; iter = iter->next) { - if(!strcmp(iter->key, request->key)) { - row = iter; - break; - } - } - - if(!row) { - row = calloc(1, sizeof(struct cache_row)); - row->key = calloc(strlen(request->key) + 1, 1); - strcpy(row->key, request->key); - row->next = cache; - cache = row; - } - - if(!row->borrowed) { - row->length = request->length; - if(row->length != 0) { - row->elements = calloc(row->length, sizeof(int32_t)); - memcpy(row->elements, request->elements, - sizeof(int32_t) * row->length); - } else { - free(row->elements); - row->elements = NULL; - } - - reply.succeeded = 1; - } else { - reply.succeeded = 0; - } - - mailbox_send(&reply); - break; - } - - default: { - core_log("Received invalid message type %d from kernel CPU\n", - umsg->type); - return 0; - } - } - - return 1; -} - -/* Returns amount of bytes consumed on success. - * Returns -1 in case of irrecoverable error - * (the session must be dropped and session_end called). - * Returns -2 if the host has requested session reset. - */ -int session_input(void *data, int length) -{ - return in_packet_fill((uint8_t*)data, length); -} - -/* *length is set to -1 in case of irrecoverable error - * (the session must be dropped and session_end called) - */ -void session_poll(void **data, int *length, int *close_flag) -{ - *close_flag = 0; - - if(user_kernel_state == USER_KERNEL_RUNNING) { - if(watchdog_expired()) { - core_log("Watchdog expired\n"); - - *close_flag = 1; - out_packet_empty(REMOTEMSG_TYPE_WATCHDOG_EXPIRED); - } - if(!rtiocrg_check()) { - core_log("RTIO clock failure\n"); - - *close_flag = 1; - out_packet_empty(REMOTEMSG_TYPE_CLOCK_FAILURE); - } - } - - if(!*close_flag) { - /* If the output buffer is available, - * check if the kernel CPU has something to transmit. - */ - if(out_packet_available()) { - struct msg_base *umsg = mailbox_receive(); - if(umsg) { - if(!process_kmsg(umsg)) { - *length = -1; - return; - } - } - } - } - - out_packet_extract(data, length); -} - -void session_ack_consumed(int length) -{ - out_packet_advance_consumed(length); -} - -void session_ack_sent(int length) -{ - out_packet_advance_sent(length); -} diff --git a/artiq/runtime/session.h b/artiq/runtime/session.h deleted file mode 100644 index 06ee421ed..000000000 --- a/artiq/runtime/session.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __SESSION_H -#define __SESSION_H - -void session_startup_kernel(void); -void session_start(void); -void session_end(void); - -int session_input(void *data, int length); -void session_poll(void **data, int *length, int *close_flag); -void session_ack_consumed(int length); -void session_ack_sent(int length); - -#endif /* __SESSION_H */ diff --git a/artiq/runtime/test_mode.c b/artiq/runtime/test_mode.c deleted file mode 100644 index 38f9c5dcb..000000000 --- a/artiq/runtime/test_mode.c +++ /dev/null @@ -1,700 +0,0 @@ -/* - * Copyright (C) 2014, 2015 M-Labs Limited - * Copyright (C) 2014, 2015 Robert Jordens - */ - -#include -#include -#include - -#include -#include -#include -#include - -#include "dds.h" -#include "flash_storage.h" -#include "bridge_ctl.h" -#include "clock.h" -#include "test_mode.h" - -#ifdef CSR_LEDS_BASE -static void leds(char *value) -{ - char *c; - unsigned int value2; - - if(*value == 0) { - printf("leds \n"); - return; - } - - value2 = strtoul(value, &c, 0); - if(*c != 0) { - printf("incorrect value\n"); - return; - } - - leds_out_write(value2); -} -#endif - -#ifdef CSR_RTIO_CRG_BASE -static void clksrc(char *value) -{ - char *c; - unsigned int value2; - - if(*value == 0) { - printf("clksrc \n"); - return; - } - - value2 = strtoul(value, &c, 0); - if(*c != 0) { - printf("incorrect value\n"); - return; - } - - rtio_crg_clock_sel_write(value2); -} -#endif - -static void ttloe(char *n, char *value) -{ - char *c; - unsigned int n2, value2; - - if((*n == 0)||(*value == 0)) { - printf("ttloe \n"); - return; - } - - n2 = strtoul(n, &c, 0); - if(*c != 0) { - printf("incorrect channel\n"); - return; - } - value2 = strtoul(value, &c, 0); - if(*c != 0) { - printf("incorrect value\n"); - return; - } - - brg_ttloe(n2, value2); -} - -static void ttlo(char *n, char *value) -{ - char *c; - unsigned int n2, value2; - - if((*n == 0)||(*value == 0)) { - printf("ttlo \n"); - return; - } - - n2 = strtoul(n, &c, 0); - if(*c != 0) { - printf("incorrect channel\n"); - return; - } - value2 = strtoul(value, &c, 0); - if(*c != 0) { - printf("incorrect value\n"); - return; - } - - brg_ttlo(n2, value2); -} - -#if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0)) - -static int bus_channel = CONFIG_RTIO_FIRST_DDS_CHANNEL; - -static void ddsbus(char *n) -{ - char *c; - unsigned int n2; - - if(*n == 0) { - printf("ddsbus \n"); - return; - } - - n2 = strtoul(n, &c, 0); - if(*c != 0) { - printf("incorrect bus channel\n"); - return; - } - - bus_channel = n2; -} - - -static void ddssel(char *n) -{ - char *c; - unsigned int n2; - - if(*n == 0) { - printf("ddssel \n"); - return; - } - - n2 = strtoul(n, &c, 0); - if(*c != 0) { - printf("incorrect channel\n"); - return; - } - -#ifdef CONFIG_DDS_ONEHOT_SEL - n2 = 1 << n2; -#endif - brg_ddssel(bus_channel, n2); -} - -static void ddsw(char *addr, char *value) -{ - char *c; - unsigned int addr2, value2; - - if((*addr == 0) || (*value == 0)) { - printf("ddsr \n"); - return; - } - - addr2 = strtoul(addr, &c, 0); - if(*c != 0) { - printf("incorrect address\n"); - return; - } - value2 = strtoul(value, &c, 0); - if(*c != 0) { - printf("incorrect value\n"); - return; - } - - brg_ddswrite(bus_channel, addr2, value2); -} - -static void ddsr(char *addr) -{ - char *c; - unsigned int addr2; - - if(*addr == 0) { - printf("ddsr \n"); - return; - } - - addr2 = strtoul(addr, &c, 0); - if(*c != 0) { - printf("incorrect address\n"); - return; - } - -#ifdef CONFIG_DDS_AD9858 - printf("0x%02x\n", brg_ddsread(bus_channel, addr2)); -#endif -#ifdef CONFIG_DDS_AD9914 - printf("0x%04x\n", brg_ddsread(bus_channel, addr2)); -#endif -} - -static void ddsfud(void) -{ - brg_ddsfud(bus_channel); -} - -static void ddsftw(char *n, char *ftw) -{ - char *c; - unsigned int n2, ftw2; - - if((*n == 0) || (*ftw == 0)) { - printf("ddsftw \n"); - return; - } - - n2 = strtoul(n, &c, 0); - if(*c != 0) { - printf("incorrect channel\n"); - return; - } - ftw2 = strtoul(ftw, &c, 0); - if(*c != 0) { - printf("incorrect value\n"); - return; - } - -#ifdef CONFIG_DDS_ONEHOT_SEL - n2 = 1 << n2; -#endif - brg_ddssel(bus_channel, n2); - -#ifdef CONFIG_DDS_AD9858 - brg_ddswrite(bus_channel, DDS_FTW0, ftw2 & 0xff); - brg_ddswrite(bus_channel, DDS_FTW1, (ftw2 >> 8) & 0xff); - brg_ddswrite(bus_channel, DDS_FTW2, (ftw2 >> 16) & 0xff); - brg_ddswrite(bus_channel, DDS_FTW3, (ftw2 >> 24) & 0xff); -#endif -#ifdef CONFIG_DDS_AD9914 - brg_ddswrite(bus_channel, DDS_FTWL, ftw2 & 0xffff); - brg_ddswrite(bus_channel, DDS_FTWH, (ftw2 >> 16) & 0xffff); -#endif - - brg_ddsfud(bus_channel); -} - -static void ddsreset(void) -{ - brg_ddsreset(bus_channel); -} - -#ifdef CONFIG_DDS_AD9858 -static void ddsinit(void) -{ - brg_ddsreset(bus_channel); - brg_ddswrite(bus_channel, DDS_CFR0, 0x78); - brg_ddswrite(bus_channel, DDS_CFR1, 0x00); - brg_ddswrite(bus_channel, DDS_CFR2, 0x00); - brg_ddswrite(bus_channel, DDS_CFR3, 0x00); - brg_ddsfud(bus_channel); -} -#endif - -#ifdef CONFIG_DDS_AD9914 -static void ddsinit(void) -{ - long long int t; - - brg_ddsreset(bus_channel); - brg_ddswrite(bus_channel, DDS_CFR1H, 0x0000); /* Enable cosine output */ - brg_ddswrite(bus_channel, DDS_CFR2L, 0x8900); /* Enable matched latency */ - brg_ddswrite(bus_channel, DDS_CFR2H, 0x0080); /* Enable profile mode */ - brg_ddswrite(bus_channel, DDS_ASF, 0x0fff); /* Set amplitude to maximum */ - brg_ddswrite(bus_channel, DDS_CFR4H, 0x0105); /* Enable DAC calibration */ - brg_ddswrite(bus_channel, DDS_FUD, 0); - t = clock_get_ms(); - while(clock_get_ms() < t + 2); - brg_ddswrite(bus_channel, DDS_CFR4H, 0x0005); /* Disable DAC calibration */ - brg_ddsfud(bus_channel); -} -#endif - -static void do_ddstest_one(unsigned int i) -{ - unsigned int v[12] = { - 0xaaaaaaaa, 0x55555555, 0xa5a5a5a5, 0x5a5a5a5a, - 0x00000000, 0xffffffff, 0x12345678, 0x87654321, - 0x0000ffff, 0xffff0000, 0x00ff00ff, 0xff00ff00, - }; - unsigned int f, g, j; - -#ifdef CONFIG_DDS_ONEHOT_SEL - brg_ddssel(bus_channel, 1 << i); -#else - brg_ddssel(bus_channel, i); -#endif - ddsinit(); - - for(j=0; j<12; j++) { - f = v[j]; -#ifdef CONFIG_DDS_AD9858 - brg_ddswrite(bus_channel, DDS_FTW0, f & 0xff); - brg_ddswrite(bus_channel, DDS_FTW1, (f >> 8) & 0xff); - brg_ddswrite(bus_channel, DDS_FTW2, (f >> 16) & 0xff); - brg_ddswrite(bus_channel, DDS_FTW3, (f >> 24) & 0xff); -#endif -#ifdef CONFIG_DDS_AD9914 - brg_ddswrite(bus_channel, DDS_FTWL, f & 0xffff); - brg_ddswrite(bus_channel, DDS_FTWH, (f >> 16) & 0xffff); -#endif - brg_ddsfud(bus_channel); -#ifdef CONFIG_DDS_AD9858 - g = brg_ddsread(bus_channel, DDS_FTW0); - g |= brg_ddsread(bus_channel, DDS_FTW1) << 8; - g |= brg_ddsread(bus_channel, DDS_FTW2) << 16; - g |= brg_ddsread(bus_channel, DDS_FTW3) << 24; -#endif -#ifdef CONFIG_DDS_AD9914 - g = brg_ddsread(bus_channel, DDS_FTWL); - g |= brg_ddsread(bus_channel, DDS_FTWH) << 16; -#endif - if(g != f) - printf("readback fail on DDS %d, 0x%08x != 0x%08x\n", i, g, f); - } -} - -static void ddstest(char *n, char *channel) -{ - int i, j; - char *c; - unsigned int n2; - int channel2; - - if((*n == 0) || (*channel == 0)) { - printf("ddstest \n"); - return; - } - n2 = strtoul(n, &c, 0); - if(*c != 0) { - printf("incorrect cycles\n"); - return; - } - if(strcmp(channel, "all") == 0) - channel2 = -1; - else { - channel2 = strtoul(channel, &c, 0); - if(*c != 0) { - printf("incorrect channel\n"); - return; - } - } - - if(channel2 >= 0) { - for(i=0;i 0) - printf("KO[%u] remain == %u, expected 0\n", testnum, remain); - if(readlength != length) - printf("KO[%u] read length == %u, expected %u\n", testnum, readlength, length); - if(remain == 0 && readlength == length) - printf("."); - - readbuf[readlength] = 0; - if(memcmp(expected, readbuf, readlength) == 0) - printf(".\n"); - else - printf("KO[%u] read %s instead of %s\n", testnum, readbuf, expected); -} - -static void check_doesnt_exist(char *key, unsigned int testnum) -{ - char readbuf; - unsigned int remain, readlength; - - readlength = fs_read(key, &readbuf, sizeof(readbuf), &remain); - if(remain > 0) - printf("KO[%u] remain == %u, expected 0\n", testnum, remain); - if(readlength > 0) - printf("KO[%u] readlength == %d, expected 0\n", testnum, readlength); - if(remain == 0 && readlength == 0) - printf(".\n"); -} - -static void check_write(unsigned int ret) -{ - if(!ret) - printf("KO"); - else - printf("."); -} - -static inline void test_sector_is_full(void) -{ - char c; - char value[4096]; - char key[2] = {0, 0}; - - fs_erase(); - memset(value, '@', sizeof(value)); - for(c = 1; c <= CONFIG_SPIFLASH_SECTOR_SIZE/sizeof(value); c++) { - key[0] = c; - check_write(fs_write(key, value, sizeof(value) - 6)); - } - check_write(!fs_write("this_should_fail", "fail", 5)); - printf("\n"); -} - -static void test_one_big_record(int testnum) -{ - char value[CONFIG_SPIFLASH_SECTOR_SIZE]; - memset(value, '@', sizeof(value)); - - fs_erase(); - check_write(fs_write("a", value, sizeof(value) - 6)); - check_read("a", value, sizeof(value) - 6, testnum); - check_write(fs_write("a", value, sizeof(value) - 6)); - check_read("a", value, sizeof(value) - 6, testnum); - check_write(!fs_write("b", value, sizeof(value) - 6)); - check_read("a", value, sizeof(value) - 6, testnum); - fs_remove("a"); - check_doesnt_exist("a", testnum); - check_write(fs_write("a", value, sizeof(value) - 6)); - check_read("a", value, sizeof(value) - 6, testnum); - fs_remove("a"); - check_doesnt_exist("a", testnum); - value[0] = '!'; - check_write(fs_write("b", value, sizeof(value) - 6)); - check_read("b", value, sizeof(value) - 6, testnum); -} - -static void test_flush_duplicate_rollback(int testnum) -{ - char value[CONFIG_SPIFLASH_SECTOR_SIZE]; - memset(value, '@', sizeof(value)); - - fs_erase(); - /* This makes the flash storage full with one big record */ - check_write(fs_write("a", value, CONFIG_SPIFLASH_SECTOR_SIZE - 6)); - /* This should trigger the try_to_flush_duplicate code which - * at first will not keep the old "a" record value because we are - * overwriting it. But then it should roll back to the old value - * because the new record is too large. - */ - value[0] = '!'; - check_write(!fs_write("a", value, sizeof(value))); - /* check we still have the old record value */ - value[0] = '@'; - check_read("a", value, CONFIG_SPIFLASH_SECTOR_SIZE - 6, testnum); -} - -static void test_too_big_fails(int testnum) -{ - char value[CONFIG_SPIFLASH_SECTOR_SIZE]; - memset(value, '@', sizeof(value)); - - fs_erase(); - check_write(!fs_write("a", value, sizeof(value) - 6 + /* TOO BIG */ 1)); - check_doesnt_exist("a", testnum); -} - -static void fs_test(void) -{ - int i; - char writebuf[] = "abcdefghijklmnopqrst"; - char read_check[4096]; - int vect_length = sizeof(writebuf); - - memset(read_check, '@', sizeof(read_check)); - printf("testing...\n"); - for(i = 0; i < vect_length; i++) { - printf("%u.0:", i); - fs_erase(); - check_write(fs_write("a", writebuf, i)); - check_read("a", writebuf, i, i); - - printf("%u.1:", i); - fsfull(); - check_read("a", writebuf, i, i); - - printf("%u.2:", i); - check_read("plip", read_check, sizeof(read_check), i); - - printf("%u.3:", i); - check_write(fs_write("a", "b", 2)); - check_read("a", "b", 2, i); - - printf("%u.4:", i); - fsfull(); - check_read("a", "b", 2, i); - - printf("%u.5:", i); - check_doesnt_exist("notfound", i); - - printf("%u.6:", i); - fs_remove("a"); - check_doesnt_exist("a", i); - - printf("%u.7:", i); - fsfull(); - check_doesnt_exist("a", i); - } - - printf("%u:", vect_length); - test_sector_is_full(); - - printf("%u:", vect_length+1); - test_one_big_record(vect_length+1); - - printf("%u:", vect_length+2); - test_flush_duplicate_rollback(vect_length+2); - - printf("%u:", vect_length+3); - test_too_big_fails(vect_length+3); -} - -#endif - -static void help(void) -{ - puts("Available commands:"); - puts("help - this message"); -#ifdef CSR_RTIO_CRG_BASE - puts("clksrc - select RTIO clock source"); -#endif - puts("ttloe - set TTL output enable"); - puts("ttlo - set TTL output value"); - puts("ddsbus - select the DDS bus RTIO channel"); - puts("ddssel - select a DDS"); - puts("ddsinit - reset, config, FUD DDS"); - puts("ddsreset - reset DDS"); - puts("ddsw - write to DDS register"); - puts("ddsr - read DDS register"); - puts("ddsfud - pulse FUD"); - puts("ddsftw - write FTW"); - puts("ddstest - perform test sequence on DDS"); -#ifdef CSR_LEDS_BASE - puts("leds - set LEDs"); -#endif -#if (defined CSR_SPIFLASH_BASE && defined CONFIG_SPIFLASH_PAGE_SIZE) - puts("fserase - erase flash storage"); - puts("fswrite - write to flash storage"); - puts("fsread - read flash storage"); - puts("fsremove - remove a key-value record from flash storage"); - puts("fstest - run flash storage tests. WARNING: erases the storage area"); -#endif -} - -static void readstr(char *s, int size) -{ - char c[2]; - int ptr; - - c[1] = 0; - ptr = 0; - while(1) { - c[0] = readchar(); - switch(c[0]) { - case 0x7f: - case 0x08: - if(ptr > 0) { - ptr--; - putsnonl("\x08 \x08"); - } - break; - case 0x07: - break; - case '\r': - case '\n': - s[ptr] = 0x00; - putsnonl("\n"); - return; - default: - putsnonl(c); - s[ptr] = c[0]; - ptr++; - break; - } - } -} - -static char *get_token(char **str) -{ - char *c, *d; - - c = (char *)strchr(*str, ' '); - if(c == NULL) { - d = *str; - *str = *str+strlen(*str); - return d; - } - *c = 0; - d = *str; - *str = c+1; - return d; -} - - -static void do_command(char *c) -{ - char *token; - - token = get_token(&c); - - if(strcmp(token, "help") == 0) help(); -#ifdef CSR_LEDS_BASE - else if(strcmp(token, "leds") == 0) leds(get_token(&c)); -#endif - -#ifdef CSR_RTIO_CRG_BASE - else if(strcmp(token, "clksrc") == 0) clksrc(get_token(&c)); -#endif - - else if(strcmp(token, "ttloe") == 0) ttloe(get_token(&c), get_token(&c)); - else if(strcmp(token, "ttlo") == 0) ttlo(get_token(&c), get_token(&c)); - -#if ((defined CONFIG_RTIO_DDS_COUNT) && (CONFIG_RTIO_DDS_COUNT > 0)) - else if(strcmp(token, "ddsbus") == 0) ddsbus(get_token(&c)); - else if(strcmp(token, "ddssel") == 0) ddssel(get_token(&c)); - else if(strcmp(token, "ddsw") == 0) ddsw(get_token(&c), get_token(&c)); - else if(strcmp(token, "ddsr") == 0) ddsr(get_token(&c)); - else if(strcmp(token, "ddsreset") == 0) ddsreset(); - else if(strcmp(token, "ddsinit") == 0) ddsinit(); - else if(strcmp(token, "ddsfud") == 0) ddsfud(); - else if(strcmp(token, "ddsftw") == 0) ddsftw(get_token(&c), get_token(&c)); - else if(strcmp(token, "ddstest") == 0) ddstest(get_token(&c), get_token(&c)); -#endif - -#if (defined CSR_SPIFLASH_BASE && defined CONFIG_SPIFLASH_PAGE_SIZE) - else if(strcmp(token, "fserase") == 0) fs_erase(); - else if(strcmp(token, "fswrite") == 0) fswrite(get_token(&c), c, strlen(c)); - else if(strcmp(token, "fsread") == 0) fsread(get_token(&c)); - else if(strcmp(token, "fsremove") == 0) fs_remove(get_token(&c)); - else if(strcmp(token, "fstest") == 0) fs_test(); -#endif - - else if(strcmp(token, "") != 0) - printf("Command not found\n"); -} - -void test_main(void) -{ - char buffer[64]; - - brg_start(); - - while(1) { - putsnonl("\e[1mtest>\e[0m "); - readstr(buffer, 64); - do_command(buffer); - } -} diff --git a/artiq/runtime/test_mode.h b/artiq/runtime/test_mode.h deleted file mode 100644 index fb719d7d4..000000000 --- a/artiq/runtime/test_mode.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __TEST_MODE_H -#define __TEST_MODE_H - -void test_main(void); - -#endif diff --git a/artiq/runtime/ttl.h b/artiq/runtime/ttl.h deleted file mode 100644 index 87fa74b7c..000000000 --- a/artiq/runtime/ttl.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __TTL_H -#define __TTL_H - -#define TTL_O_ADDR 0 -#define TTL_OE_ADDR 1 -#define TTL_SENSITIVITY_ADDR 2 - -#endif /* __TTL_H */ diff --git a/artiq/test/coredevice/test_embedding.py b/artiq/test/coredevice/test_embedding.py index 988d23e73..8ad7cef63 100644 --- a/artiq/test/coredevice/test_embedding.py +++ b/artiq/test/coredevice/test_embedding.py @@ -70,7 +70,73 @@ class DefaultArgTest(ExperimentCase): self.assertEqual(exp.run(), 42) -class _RPC(EnvExperiment): +class _RPCTypes(EnvExperiment): + def build(self): + self.setattr_device("core") + self.setattr_device("led") + + def return_bool(self) -> TBool: + return True + + def return_int32(self) -> TInt32: + return 1 + + def return_int64(self) -> TInt64: + return 0x100000000 + + def return_float(self) -> TFloat: + return 1.0 + + def return_str(self) -> TStr: + return "foo" + + def return_tuple(self) -> TTuple([TInt32, TInt32]): + return (1, 2) + + def return_list(self) -> TList(TInt32): + return [2, 3] + + def return_range(self) -> TRange32: + return range(10) + + @kernel + def run_recv(self): + core_log(self.return_bool()) + core_log(self.return_int32()) + core_log(self.return_int64()) + core_log(self.return_float()) + core_log(self.return_str()) + core_log(self.return_tuple()) + core_log(self.return_list()) + core_log(self.return_range()) + + def accept(self, value): + pass + + @kernel + def run_send(self): + self.accept(True) + self.accept(1) + self.accept(0x100000000) + self.accept(1.0) + self.accept("foo") + self.accept((2, 3)) + self.accept([1, 2]) + self.accept(range(10)) + self.accept(self) + + def run(self): + self.run_send() + self.run_recv() + + +class RPCTypesTest(ExperimentCase): + def test_args(self): + exp = self.create(_RPCTypes) + exp.run() + + +class _RPCCalls(EnvExperiment): def build(self): self.setattr_device("core") @@ -121,9 +187,9 @@ class _RPC(EnvExperiment): sleep(1.0) -class RPCTest(ExperimentCase): +class RPCCallsTest(ExperimentCase): def test_args(self): - exp = self.create(_RPC) + exp = self.create(_RPCCalls) self.assertEqual(exp.args0(), 0) self.assertEqual(exp.args1(), 1) self.assertEqual(exp.args2(), 2) @@ -151,6 +217,27 @@ class AnnotationTest(ExperimentCase): exp = self.create(_Annotation) self.assertEqual(exp.overflow(1), True) +class _Async(EnvExperiment): + def build(self): + self.setattr_device("core") + + @rpc(flags={"async"}) + def recv_async(self, data): + pass + + @kernel + def run(self): + # fast async path + self.recv_async([0]*128) + # slow async path + self.recv_async([0]*4096) + + +class AsyncTest(ExperimentCase): + def test_args(self): + exp = self.create(_RPCTypes) + exp.run() + class _Payload1MB(EnvExperiment): def build(self): diff --git a/artiq/test/libartiq_support/artiq_terminate.c b/artiq/test/libartiq_support/artiq_terminate.c index 5b1315131..be56c4b29 100644 --- a/artiq/test/libartiq_support/artiq_terminate.c +++ b/artiq/test/libartiq_support/artiq_terminate.c @@ -8,7 +8,7 @@ #include void __artiq_terminate(struct artiq_exception *exn, - struct artiq_backtrace_item *backtrace, + uintptr_t *backtrace, size_t backtrace_size) { printf("Uncaught %s: %s (%"PRIi64", %"PRIi64", %"PRIi64")\n" "at %s:%"PRIi32":%"PRIi32"\n", @@ -17,12 +17,7 @@ void __artiq_terminate(struct artiq_exception *exn, exn->file, exn->line, exn->column + 1); for(size_t i = 0; i < backtrace_size; i++) { - Dl_info info; - if(dladdr((void*) backtrace[i].function, &info) && info.dli_sname) { - printf("at %s+%p\n", info.dli_sname, (void*)backtrace[i].offset); - } else { - printf("at %p+%p\n", (void*)backtrace[i].function, (void*)backtrace[i].offset); - } + printf("at %"PRIxPTR"\n", backtrace[i]); } exit(1); diff --git a/artiq/test/libartiq_support/artiq_time.c b/artiq/test/libartiq_support/artiq_time.c index f60e46e47..18de56424 100644 --- a/artiq/test/libartiq_support/artiq_time.c +++ b/artiq/test/libartiq_support/artiq_time.c @@ -3,9 +3,9 @@ int64_t now = 0; -int watchdog_set(int ms) +int watchdog_set(long long ms) { - printf("watchdog_set %d\n", ms); + printf("watchdog_set %lld\n", ms); return ms; } diff --git a/artiq/test/lit/embedding/async_rpc.py b/artiq/test/lit/embedding/async_rpc.py new file mode 100644 index 000000000..691a093c3 --- /dev/null +++ b/artiq/test/lit/embedding/async_rpc.py @@ -0,0 +1,15 @@ +# RUN: env ARTIQ_DUMP_LLVM=%t %python -m artiq.compiler.testbench.embedding +compile %s +# RUN: OutputCheck %s --file-to-check=%t.ll + +from artiq.language.core import * +from artiq.language.types import * + +# CHECK: call void @send_async_rpc + +@rpc(flags={"async"}) +def foo(): + pass + +@kernel +def entrypoint(): + foo() diff --git a/artiq/test/lit/embedding/error_rpc_async_return.py b/artiq/test/lit/embedding/error_rpc_async_return.py new file mode 100644 index 000000000..72f3c79c3 --- /dev/null +++ b/artiq/test/lit/embedding/error_rpc_async_return.py @@ -0,0 +1,15 @@ +# RUN: %python -m artiq.compiler.testbench.embedding +diag %s 2>%t +# RUN: OutputCheck %s --file-to-check=%t + +from artiq.language.core import * +from artiq.language.types import * + +# CHECK-L: ${LINE:+2}: fatal: functions that return a value cannot be defined as async RPCs +@rpc(flags={"async"}) +def foo() -> TInt32: + pass + +@kernel +def entrypoint(): + # CHECK-L: ${LINE:+1}: note: function called here + foo() diff --git a/artiq/test/lit/integration/for.py b/artiq/test/lit/integration/for.py index 9c305f5da..7156bee5d 100644 --- a/artiq/test/lit/integration/for.py +++ b/artiq/test/lit/integration/for.py @@ -27,3 +27,27 @@ for x in range(10): assert False else: assert False + +# Verify continue target is reset in else block. +cond = False +while True: + if cond: + break + cond = True + for _ in range(1): + pass + else: + continue + assert False +else: + assert False + +# Verify for target is reset in else block. +while True: + for _ in range(1): + pass + else: + break + assert False +else: + assert False diff --git a/artiq/test/lit/integration/while.py b/artiq/test/lit/integration/while.py index 7cd2ac626..e7d03c0d7 100644 --- a/artiq/test/lit/integration/while.py +++ b/artiq/test/lit/integration/while.py @@ -29,3 +29,27 @@ while cond: cond = False continue assert False + +# Verify continue target is reset in else block. +cond = False +while True: + if cond: + break + cond = True + while False: + assert False + else: + continue + assert False +else: + assert False + +# Verify break target is reset in else block. +while True: + while False: + assert False + else: + break + assert False +else: + assert False diff --git a/conda/artiq-kc705-nist_clock/meta.yaml b/conda/artiq-kc705-nist_clock/meta.yaml index 673f61257..853a5cfae 100644 --- a/conda/artiq-kc705-nist_clock/meta.yaml +++ b/conda/artiq-kc705-nist_clock/meta.yaml @@ -13,7 +13,7 @@ build: requirements: build: - migen 0.4 - - misoc 0.3 + - misoc 0.4 - llvm-or1k - binutils-or1k-linux >=2.27 - rust-core-or1k diff --git a/conda/artiq-kc705-nist_qc1/meta.yaml b/conda/artiq-kc705-nist_qc1/meta.yaml index aa577ef8d..d5be16c2e 100644 --- a/conda/artiq-kc705-nist_qc1/meta.yaml +++ b/conda/artiq-kc705-nist_qc1/meta.yaml @@ -13,7 +13,7 @@ build: requirements: build: - migen 0.4 - - misoc 0.3 + - misoc 0.4 - llvm-or1k - binutils-or1k-linux >=2.27 - rust-core-or1k diff --git a/conda/artiq-kc705-nist_qc2/meta.yaml b/conda/artiq-kc705-nist_qc2/meta.yaml index 7dce4b74f..54e5d2579 100644 --- a/conda/artiq-kc705-nist_qc2/meta.yaml +++ b/conda/artiq-kc705-nist_qc2/meta.yaml @@ -13,7 +13,7 @@ build: requirements: build: - migen 0.4 - - misoc 0.3 + - misoc 0.4 - llvm-or1k - binutils-or1k-linux >=2.27 - rust-core-or1k diff --git a/conda/artiq-pipistrello-nist_qc1/meta.yaml b/conda/artiq-pipistrello-nist_qc1/meta.yaml index 14241a705..a69497fff 100644 --- a/conda/artiq-pipistrello-nist_qc1/meta.yaml +++ b/conda/artiq-pipistrello-nist_qc1/meta.yaml @@ -13,7 +13,7 @@ build: requirements: build: - migen 0.4 - - misoc 0.3 + - misoc 0.4 - llvm-or1k - binutils-or1k-linux >=2.27 - rust-core-or1k diff --git a/doc/manual/compiler.rst b/doc/manual/compiler.rst index 6a8271a5f..8185b2611 100644 --- a/doc/manual/compiler.rst +++ b/doc/manual/compiler.rst @@ -47,6 +47,19 @@ The Python types correspond to ARTIQ type annotations as follows: | range | TRange32, TRange64 | +-------------+-------------------------+ +Asynchronous RPCs +----------------- + +If an RPC returns no value, it can be invoked in a way that does not block until the RPC finishes +execution, but only until it is queued. (Submitting asynchronous RPCs too rapidly, as well as +submitting asynchronous RPCs with arguments that are too large, can still block until completion.) + +To define an asynchronous RPC, use the ``@rpc`` annotation with a flag: + + @rpc(flags={"async"}) + def record_result(x): + self.results.append(x) + Additional optimizations ------------------------ diff --git a/doc/manual/installing_from_source.rst b/doc/manual/installing_from_source.rst index 2836e085c..32d1e3481 100644 --- a/doc/manual/installing_from_source.rst +++ b/doc/manual/installing_from_source.rst @@ -33,7 +33,7 @@ and the ARTIQ kernels. $ wget https://ftp.gnu.org/gnu/binutils/binutils-2.27.tar.bz2 $ tar xvf binutils-2.27.tar.bz2 $ cd binutils-2.27 - $ curl -L 'https://github.com/m-labs/conda-recipes/blob/ece4cefbcce5548c5bd7fd4740d71ecd6930065e/conda/binutils-or1k-linux/fix-R_OR1K_GOTOFF-relocations.patch' | patch -p1 + $ curl -L https://raw.githubusercontent.com/m-labs/conda-recipes/ece4cefbcce5548c5bd7fd4740d71ecd6930065e/conda/binutils-or1k-linux/fix-R_OR1K_GOTOFF-relocations.patch' | patch -p1 $ mkdir build $ cd build @@ -62,18 +62,24 @@ and the ARTIQ kernels. $ cd ~/artiq-dev $ git clone https://github.com/m-labs/rust + $ cd rust $ git checkout artiq + $ git submodule update --init $ mkdir build $ cd build - $ ../configure --prefix=/usr/local/rust-or1k --llvm-root=/usr/local/llvm-or1k/bin/llvm-config --disable-manage-submodules + $ ../configure --prefix=/usr/local/rust-or1k --llvm-root=/usr/local/llvm-or1k --disable-manage-submodules $ sudo make install -j4 - $ libs="libcore liballoc librustc_unicode libcollections liblibc_mini libunwind libpanic_unwind" + $ libs="libcore liballoc librustc_unicode libcollections liblibc_mini libunwind" + $ rustc="/usr/local/rust-or1k/bin/rustc --target or1k-unknown-none -g -C target-feature=+mul,+div,+ffl1,+cmov,+addc -C opt-level=s -L ." + $ destdir="/usr/local/rust-or1k/lib/rustlib/or1k-unknown-none/lib/" $ mkdir ../build-or1k $ cd ../build-or1k - $ for lib in ${libs}; do /usr/local/rust-or1k/bin/rustc src/${lib}/lib.rs; done - $ /usr/local/rust-or1k/bin/rustc -Cpanic=abort src/libpanic_abort/lib.rs - $ sudo cp * /usr/local/rust-or1k/lib/rustlib/or1k-unknown-none/lib/ + $ for lib in ${libs}; do ${rustc} ../src/${lib}/lib.rs; done + $ ${rustc} -Cpanic=abort ../src/libpanic_abort/lib.rs + $ ${rustc} -Cpanic=unwind ../src/libpanic_unwind/lib.rs --cfg llvm_libunwind + $ sudo mkdir -p ${destdir} + $ sudo cp *.rlib ${destdir} .. note:: Compilation of LLVM can take more than 30 min on some machines. Compilation of Rust can take more than two hours. @@ -167,7 +173,7 @@ These steps are required to generate gateware bitstream (``.bit``) files, build $ python3.5 -m artiq.gateware.targets.kc705 -H nist_qc1 # or nist_qc2 - .. note:: Add ``--toolchain vivado`` if you wish to use Vivado instead of ISE. + .. note:: Add ``--toolchain ise`` if you wish to use ISE instead of Vivado. * Then, gather the binaries and flash them: :: diff --git a/doc/manual/management_system.rst b/doc/manual/management_system.rst index bb51aa699..c28e612cf 100644 --- a/doc/manual/management_system.rst +++ b/doc/manual/management_system.rst @@ -72,7 +72,9 @@ To check whether ``pause()`` would in fact *not* return immediately, use :meth:` The experiment must place the hardware in a safe state and disconnect from the core device (typically, by using ``self.core.comm.close()``) before calling ``pause``. -Accessing the ``pause`` method is done through a virtual device called ``scheduler`` that is accessible to all experiments. The scheduler virtual device is requested like regular devices using ``get_device`` or ``attr_device``. +Accessing the ``pause`` and ``check_pause`` methods is done through a virtual device called ``scheduler`` that is accessible to all experiments. The scheduler virtual device is requested like regular devices using ``get_device`` or ``attr_device``. + +``check_pause`` can be called (via RPC) from a kernel, but ``pause`` must not. Multiple pipelines ------------------