forked from M-Labs/artiq
Implement syscalls for the new compiler.
This commit is contained in:
parent
435559fe50
commit
c72267ecf5
|
@ -238,6 +238,10 @@ class Stitcher:
|
|||
name = function.__code__.co_name
|
||||
|
||||
source_line = linecache.getline(filename, line)
|
||||
while source_line.lstrip().startswith("@"):
|
||||
line += 1
|
||||
source_line = linecache.getline(filename, line)
|
||||
|
||||
column = re.search("def", source_line).start(0)
|
||||
source_buffer = source.Buffer(source_line, filename, line)
|
||||
return source.Range(source_buffer, column, column)
|
||||
|
@ -248,11 +252,16 @@ class Stitcher:
|
|||
{"function": function.__name__},
|
||||
self._function_loc(function))
|
||||
|
||||
def _extract_annot(self, function, annot, kind, call_loc):
|
||||
def _extract_annot(self, function, annot, kind, call_loc, is_syscall):
|
||||
if not isinstance(annot, types.Type):
|
||||
note = diagnostic.Diagnostic("note",
|
||||
"in function called remotely here", {},
|
||||
call_loc)
|
||||
if is_syscall:
|
||||
note = diagnostic.Diagnostic("note",
|
||||
"in system call here", {},
|
||||
call_loc)
|
||||
else:
|
||||
note = diagnostic.Diagnostic("note",
|
||||
"in function called remotely here", {},
|
||||
call_loc)
|
||||
diag = diagnostic.Diagnostic("error",
|
||||
"type annotation for {kind}, '{annot}', is not an ARTIQ type",
|
||||
{"kind": kind, "annot": repr(annot)},
|
||||
|
@ -264,11 +273,19 @@ class Stitcher:
|
|||
else:
|
||||
return annot
|
||||
|
||||
def _type_of_param(self, function, loc, param):
|
||||
def _type_of_param(self, function, loc, param, is_syscall):
|
||||
if param.annotation is not inspect.Parameter.empty:
|
||||
# Type specified explicitly.
|
||||
return self._extract_annot(function, param.annotation,
|
||||
"argument {}".format(param.name), loc)
|
||||
"argument '{}'".format(param.name), loc,
|
||||
is_syscall)
|
||||
elif is_syscall:
|
||||
# Syscalls must be entirely annotated.
|
||||
diag = diagnostic.Diagnostic("error",
|
||||
"system call argument '{argument}' must have a type annotation",
|
||||
{"argument": param.name},
|
||||
self._function_loc(function))
|
||||
self.engine.process(diag)
|
||||
elif param.default is not inspect.Parameter.empty:
|
||||
# Try and infer the type from the default value.
|
||||
# This is tricky, because the default value might not have
|
||||
|
@ -281,8 +298,8 @@ class Stitcher:
|
|||
def proxy_diagnostic(diag):
|
||||
note = diagnostic.Diagnostic("note",
|
||||
"expanded from here while trying to infer a type for an"
|
||||
" unannotated optional argument '{param_name}' from its default value",
|
||||
{"param_name": param.name},
|
||||
" unannotated optional argument '{argument}' from its default value",
|
||||
{"argument": param.name},
|
||||
loc)
|
||||
diag.notes.append(note)
|
||||
|
||||
|
@ -300,7 +317,7 @@ class Stitcher:
|
|||
# Let the rest of the program decide.
|
||||
return types.TVar()
|
||||
|
||||
def _quote_rpc_function(self, function, loc):
|
||||
def _quote_foreign_function(self, function, loc, syscall):
|
||||
signature = inspect.signature(function)
|
||||
|
||||
arg_types = OrderedDict()
|
||||
|
@ -318,44 +335,73 @@ class Stitcher:
|
|||
continue
|
||||
|
||||
if param.default is inspect.Parameter.empty:
|
||||
arg_types[param.name] = self._type_of_param(function, loc, param)
|
||||
arg_types[param.name] = self._type_of_param(function, loc, param,
|
||||
is_syscall=syscall is not None)
|
||||
elif syscall is None:
|
||||
optarg_types[param.name] = self._type_of_param(function, loc, param,
|
||||
is_syscall=syscall is not None)
|
||||
else:
|
||||
optarg_types[param.name] = self._type_of_param(function, loc, param)
|
||||
diag = diagnostic.Diagnostic("error",
|
||||
"system call argument '{argument}' must not have a default value",
|
||||
{"argument": param.name},
|
||||
self._function_loc(function))
|
||||
self.engine.process(diag)
|
||||
|
||||
if signature.return_annotation is not inspect.Signature.empty:
|
||||
ret_type = self._extract_annot(function, signature.return_annotation,
|
||||
"return type", loc)
|
||||
else:
|
||||
diag = diagnostic.Diagnostic("fatal",
|
||||
"function must have a return type specified to be called remotely", {},
|
||||
"return type", loc, is_syscall=syscall is not None)
|
||||
elif syscall is None:
|
||||
diag = diagnostic.Diagnostic("error",
|
||||
"function must have a return type annotation to be called remotely", {},
|
||||
self._function_loc(function))
|
||||
self.engine.process(diag)
|
||||
ret_type = types.TVar()
|
||||
else: # syscall is not None
|
||||
diag = diagnostic.Diagnostic("error",
|
||||
"system call must have a return type annotation", {},
|
||||
self._function_loc(function))
|
||||
self.engine.process(diag)
|
||||
ret_type = types.TVar()
|
||||
|
||||
rpc_type = types.TRPCFunction(arg_types, optarg_types, ret_type,
|
||||
service=self._map(function))
|
||||
if syscall is None:
|
||||
function_type = types.TRPCFunction(arg_types, optarg_types, ret_type,
|
||||
service=self._map(function))
|
||||
function_name = "__rpc_{}__".format(function_type.service)
|
||||
else:
|
||||
function_type = types.TCFunction(arg_types, ret_type,
|
||||
name=syscall)
|
||||
function_name = "__ffi_{}__".format(function_type.name)
|
||||
|
||||
rpc_name = "__rpc_{}__".format(rpc_type.service)
|
||||
self.globals[rpc_name] = rpc_type
|
||||
self.functions[function] = rpc_name
|
||||
self.globals[function_name] = function_type
|
||||
self.functions[function] = function_name
|
||||
|
||||
return rpc_name
|
||||
return function_name
|
||||
|
||||
def _quote_function(self, function, loc):
|
||||
if function in self.functions:
|
||||
return self.functions[function]
|
||||
|
||||
if hasattr(function, "artiq_embedded"):
|
||||
# Insert the typed AST for the new function and restart inference.
|
||||
# It doesn't really matter where we insert as long as it is before
|
||||
# the final call.
|
||||
function_node = self._quote_embedded_function(function)
|
||||
self.typedtree.insert(0, function_node)
|
||||
self.inference_finished = False
|
||||
return function_node.name
|
||||
if function.artiq_embedded.function is not None:
|
||||
# Insert the typed AST for the new function and restart inference.
|
||||
# It doesn't really matter where we insert as long as it is before
|
||||
# the final call.
|
||||
function_node = self._quote_embedded_function(function)
|
||||
self.typedtree.insert(0, function_node)
|
||||
self.inference_finished = False
|
||||
return function_node.name
|
||||
elif function.artiq_embedded.syscall is not None:
|
||||
# Insert a storage-less global whose type instructs the compiler
|
||||
# to perform a system call instead of a regular call.
|
||||
return self._quote_foreign_function(function, loc,
|
||||
syscall=function.artiq_embedded.syscall)
|
||||
else:
|
||||
assert False
|
||||
else:
|
||||
# Insert a storage-less global whose type instructs the compiler
|
||||
# to perform an RPC instead of a regular call.
|
||||
return self._quote_rpc_function(function, loc)
|
||||
return self._quote_foreign_function(function, loc,
|
||||
syscall=None)
|
||||
|
||||
def stitch_call(self, function, args, kwargs):
|
||||
function_node = self._quote_embedded_function(function)
|
||||
|
|
|
@ -175,7 +175,7 @@ class LLVMIRGenerator:
|
|||
typ = typ.find()
|
||||
if types.is_tuple(typ):
|
||||
return ll.LiteralStructType([self.llty_of_type(eltty) for eltty in typ.elts])
|
||||
elif types.is_rpc_function(typ):
|
||||
elif types.is_rpc_function(typ) or types.is_c_function(typ):
|
||||
if for_return:
|
||||
return llvoid
|
||||
else:
|
||||
|
@ -731,11 +731,20 @@ class LLVMIRGenerator:
|
|||
return llvalue
|
||||
|
||||
def _prepare_closure_call(self, insn):
|
||||
llclosure, llargs = self.map(insn.target_function()), map(self.map, insn.arguments())
|
||||
llenv = self.llbuilder.extract_value(llclosure, 0)
|
||||
llfun = self.llbuilder.extract_value(llclosure, 1)
|
||||
llclosure = self.map(insn.target_function())
|
||||
llargs = [self.map(arg) for arg in insn.arguments()]
|
||||
llenv = self.llbuilder.extract_value(llclosure, 0)
|
||||
llfun = self.llbuilder.extract_value(llclosure, 1)
|
||||
return llfun, [llenv] + list(llargs)
|
||||
|
||||
def _prepare_ffi_call(self, insn):
|
||||
llargs = [self.map(arg) for arg in insn.arguments()]
|
||||
llfunty = ll.FunctionType(self.llty_of_type(insn.type, for_return=True),
|
||||
[llarg.type for llarg in llargs])
|
||||
llfun = ll.Function(self.llmodule, llfunty,
|
||||
insn.target_function().type.name)
|
||||
return llfun, list(llargs)
|
||||
|
||||
# See session.c:{send,receive}_rpc_value and comm_generic.py:_{send,receive}_rpc_value.
|
||||
def _rpc_tag(self, typ, error_handler):
|
||||
if types.is_tuple(typ):
|
||||
|
@ -869,6 +878,10 @@ class LLVMIRGenerator:
|
|||
insn.target_function().type,
|
||||
insn.arguments(),
|
||||
llnormalblock=None, llunwindblock=None)
|
||||
elif types.is_c_function(insn.target_function().type):
|
||||
llfun, llargs = self._prepare_ffi_call(insn)
|
||||
return self.llbuilder.call(llfun, llargs,
|
||||
name=insn.name)
|
||||
else:
|
||||
llfun, llargs = self._prepare_closure_call(insn)
|
||||
return self.llbuilder.call(llfun, llargs,
|
||||
|
@ -882,6 +895,10 @@ class LLVMIRGenerator:
|
|||
insn.target_function().type,
|
||||
insn.arguments(),
|
||||
llnormalblock, llunwindblock)
|
||||
elif types.is_c_function(insn.target_function().type):
|
||||
llfun, llargs = self._prepare_ffi_call(insn)
|
||||
return self.llbuilder.invoke(llfun, llargs, llnormalblock, llunwindblock,
|
||||
name=insn.name)
|
||||
else:
|
||||
llfun, llargs = self._prepare_closure_call(insn)
|
||||
return self.llbuilder.invoke(llfun, llargs, llnormalblock, llunwindblock,
|
||||
|
|
|
@ -258,6 +258,26 @@ class TRPCFunction(TFunction):
|
|||
else:
|
||||
raise UnificationError(self, other)
|
||||
|
||||
class TCFunction(TFunction):
|
||||
"""
|
||||
A function type of a runtime-provided C function.
|
||||
|
||||
:ivar name: (str) C function name
|
||||
"""
|
||||
|
||||
def __init__(self, args, ret, name):
|
||||
super().__init__(args, OrderedDict(), ret)
|
||||
self.name = name
|
||||
|
||||
def unify(self, other):
|
||||
if isinstance(other, TCFunction) and \
|
||||
self.name == other.name:
|
||||
super().unify(other)
|
||||
elif isinstance(other, TVar):
|
||||
other.unify(self)
|
||||
else:
|
||||
raise UnificationError(self, other)
|
||||
|
||||
class TBuiltin(Type):
|
||||
"""
|
||||
An instance of builtin type. Every instance of a builtin
|
||||
|
@ -371,6 +391,9 @@ def is_function(typ):
|
|||
def is_rpc_function(typ):
|
||||
return isinstance(typ.find(), TRPCFunction)
|
||||
|
||||
def is_c_function(typ):
|
||||
return isinstance(typ.find(), TCFunction)
|
||||
|
||||
def is_builtin(typ, name=None):
|
||||
typ = typ.find()
|
||||
if name is None:
|
||||
|
@ -423,7 +446,7 @@ class TypePrinter(object):
|
|||
return "(%s,)" % self.name(typ.elts[0])
|
||||
else:
|
||||
return "(%s)" % ", ".join(list(map(self.name, typ.elts)))
|
||||
elif isinstance(typ, (TFunction, TRPCFunction)):
|
||||
elif isinstance(typ, (TFunction, TRPCFunction, TCFunction)):
|
||||
args = []
|
||||
args += [ "%s:%s" % (arg, self.name(typ.args[arg])) for arg in typ.args]
|
||||
args += ["?%s:%s" % (arg, self.name(typ.optargs[arg])) for arg in typ.optargs]
|
||||
|
@ -431,6 +454,8 @@ class TypePrinter(object):
|
|||
|
||||
if isinstance(typ, TRPCFunction):
|
||||
return "rpc({}) {}".format(typ.service, signature)
|
||||
if isinstance(typ, TCFunction):
|
||||
return "ffi({}) {}".format(repr(typ.name), signature)
|
||||
elif isinstance(typ, TFunction):
|
||||
return signature
|
||||
elif isinstance(typ, TBuiltinFunction):
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import sys, tempfile
|
||||
import sys
|
||||
|
||||
from pythonparser import diagnostic
|
||||
|
||||
from artiq.language.core import *
|
||||
from artiq.language.units import ns
|
||||
from artiq.language.types import *
|
||||
|
||||
from artiq.compiler import Stitcher, Module
|
||||
from artiq.compiler.targets import OR1KTarget
|
||||
|
@ -15,6 +15,11 @@ from artiq.coredevice import exceptions
|
|||
class CompileError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
@syscall
|
||||
def rtio_get_counter() -> TInt64:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
class Core:
|
||||
def __init__(self, dmgr, ref_period=8*ns, external_clock=False):
|
||||
self.comm = dmgr.get("comm")
|
||||
|
@ -58,8 +63,8 @@ class Core:
|
|||
|
||||
@kernel
|
||||
def get_rtio_counter_mu(self):
|
||||
return syscall("rtio_get_counter")
|
||||
return rtio_get_counter()
|
||||
|
||||
@kernel
|
||||
def break_realtime(self):
|
||||
at_mu(syscall("rtio_get_counter") + 125000)
|
||||
at_mu(rtio_get_counter() + 125000)
|
||||
|
|
|
@ -9,6 +9,24 @@ PHASE_MODE_ABSOLUTE = 1
|
|||
PHASE_MODE_TRACKING = 2
|
||||
|
||||
|
||||
@syscall
|
||||
def dds_init(time_mu: TInt64, channel: TInt32) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
@syscall
|
||||
def dds_batch_enter(time_mu: TInt64) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
@syscall
|
||||
def dds_batch_exit() -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
@syscall
|
||||
def dds_set(time_mu: TInt64, channel: TInt32, ftw: TInt32,
|
||||
pow: TInt32, phase_mode: TInt32) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
|
||||
class _BatchContextManager:
|
||||
def __init__(self, dds_bus):
|
||||
self.dds_bus = dds_bus
|
||||
|
@ -34,13 +52,13 @@ class DDSBus:
|
|||
def batch_enter(self):
|
||||
"""Starts a DDS command batch. All DDS commands are buffered
|
||||
after this call, until ``batch_exit`` is called."""
|
||||
syscall("dds_batch_enter", now_mu())
|
||||
dds_batch_enter(now_mu())
|
||||
|
||||
@kernel
|
||||
def batch_exit(self):
|
||||
"""Ends a DDS command batch. All buffered DDS commands are issued
|
||||
on the bus, and FUD is pulsed at the time the batch started."""
|
||||
syscall("dds_batch_exit")
|
||||
dds_batch_exit()
|
||||
|
||||
|
||||
class _DDSGeneric:
|
||||
|
@ -91,7 +109,7 @@ class _DDSGeneric:
|
|||
"""Resets and initializes the DDS channel.
|
||||
|
||||
The runtime does this for all channels upon core device startup."""
|
||||
syscall("dds_init", now_mu(), self.channel)
|
||||
dds_init(now_mu(), self.channel)
|
||||
|
||||
@kernel
|
||||
def set_phase_mode(self, phase_mode):
|
||||
|
@ -128,7 +146,7 @@ class _DDSGeneric:
|
|||
"""
|
||||
if phase_mode == _PHASE_MODE_DEFAULT:
|
||||
phase_mode = self.phase_mode
|
||||
syscall("dds_set", now_mu(), self.channel,
|
||||
dds_set(now_mu(), self.channel,
|
||||
frequency, round(phase*2**self.pow_width), phase_mode)
|
||||
|
||||
@kernel
|
||||
|
|
|
@ -1,134 +0,0 @@
|
|||
import os
|
||||
|
||||
import llvmlite_or1k.ir as ll
|
||||
import llvmlite_or1k.binding as llvm
|
||||
|
||||
from artiq.language import units
|
||||
|
||||
_syscalls = {
|
||||
"now_init": "n:I",
|
||||
"now_save": "I:n",
|
||||
"watchdog_set": "i:i",
|
||||
"watchdog_clear": "i:n",
|
||||
"rtio_get_counter": "n:I",
|
||||
"ttl_set_o": "Iib:n",
|
||||
"ttl_set_oe": "Iib:n",
|
||||
"ttl_set_sensitivity": "Iii:n",
|
||||
"ttl_get": "iI:I",
|
||||
"ttl_clock_set": "Iii:n",
|
||||
"dds_init": "Ii:n",
|
||||
"dds_batch_enter": "I:n",
|
||||
"dds_batch_exit": "n:n",
|
||||
"dds_set": "Iiiii:n",
|
||||
}
|
||||
|
||||
|
||||
def _chr_to_type(c):
|
||||
if c == "n":
|
||||
return ll.VoidType()
|
||||
if c == "b":
|
||||
return ll.IntType(1)
|
||||
if c == "i":
|
||||
return ll.IntType(32)
|
||||
if c == "I":
|
||||
return ll.IntType(64)
|
||||
raise ValueError
|
||||
|
||||
|
||||
def _str_to_functype(s):
|
||||
assert(s[-2] == ":")
|
||||
type_ret = _chr_to_type(s[-1])
|
||||
type_args = [_chr_to_type(c) for c in s[:-2] if c != "n"]
|
||||
return ll.FunctionType(type_ret, type_args)
|
||||
|
||||
|
||||
def _chr_to_value(c):
|
||||
if c == "n":
|
||||
return base_types.VNone()
|
||||
if c == "b":
|
||||
return base_types.VBool()
|
||||
if c == "i":
|
||||
return base_types.VInt()
|
||||
if c == "I":
|
||||
return base_types.VInt(64)
|
||||
raise ValueError
|
||||
|
||||
|
||||
def _value_to_str(v):
|
||||
if isinstance(v, base_types.VNone):
|
||||
return "n"
|
||||
if isinstance(v, base_types.VBool):
|
||||
return "b"
|
||||
if isinstance(v, base_types.VInt):
|
||||
if v.nbits == 32:
|
||||
return "i"
|
||||
if v.nbits == 64:
|
||||
return "I"
|
||||
raise ValueError
|
||||
if isinstance(v, base_types.VFloat):
|
||||
return "f"
|
||||
if isinstance(v, fractions.VFraction):
|
||||
return "F"
|
||||
if isinstance(v, lists.VList):
|
||||
return "l" + _value_to_str(v.el_type)
|
||||
raise ValueError
|
||||
|
||||
|
||||
class LinkInterface:
|
||||
def init_module(self, module):
|
||||
self.module = module
|
||||
llvm_module = self.module.llvm_module
|
||||
|
||||
# RPC
|
||||
func_type = ll.FunctionType(ll.IntType(32), [ll.IntType(32)],
|
||||
var_arg=1)
|
||||
self.rpc = ll.Function(llvm_module, func_type, "__syscall_rpc")
|
||||
|
||||
# syscalls
|
||||
self.syscalls = dict()
|
||||
for func_name, func_type_str in _syscalls.items():
|
||||
func_type = _str_to_functype(func_type_str)
|
||||
self.syscalls[func_name] = ll.Function(
|
||||
llvm_module, func_type, "__syscall_" + func_name)
|
||||
|
||||
def _build_rpc(self, args, builder):
|
||||
r = base_types.VInt()
|
||||
if builder is not None:
|
||||
new_args = []
|
||||
new_args.append(args[0].auto_load(builder)) # RPC number
|
||||
for arg in args[1:]:
|
||||
# type tag
|
||||
arg_type_str = _value_to_str(arg)
|
||||
arg_type_int = 0
|
||||
for c in reversed(arg_type_str):
|
||||
arg_type_int <<= 8
|
||||
arg_type_int |= ord(c)
|
||||
new_args.append(ll.Constant(ll.IntType(32), arg_type_int))
|
||||
|
||||
# pointer to value
|
||||
if not isinstance(arg, base_types.VNone):
|
||||
if isinstance(arg.llvm_value.type, ll.PointerType):
|
||||
new_args.append(arg.llvm_value)
|
||||
else:
|
||||
arg_ptr = arg.new()
|
||||
arg_ptr.alloca(builder)
|
||||
arg_ptr.auto_store(builder, arg.llvm_value)
|
||||
new_args.append(arg_ptr.llvm_value)
|
||||
# end marker
|
||||
new_args.append(ll.Constant(ll.IntType(32), 0))
|
||||
r.auto_store(builder, builder.call(self.rpc, new_args))
|
||||
return r
|
||||
|
||||
def _build_regular_syscall(self, syscall_name, args, builder):
|
||||
r = _chr_to_value(_syscalls[syscall_name][-1])
|
||||
if builder is not None:
|
||||
args = [arg.auto_load(builder) for arg in args]
|
||||
r.auto_store(builder, builder.call(self.syscalls[syscall_name],
|
||||
args))
|
||||
return r
|
||||
|
||||
def build_syscall(self, syscall_name, args, builder):
|
||||
if syscall_name == "rpc":
|
||||
return self._build_rpc(args, builder)
|
||||
else:
|
||||
return self._build_regular_syscall(syscall_name, args, builder)
|
|
@ -1,4 +1,26 @@
|
|||
from artiq.language.core import *
|
||||
from artiq.language.types import *
|
||||
|
||||
|
||||
@syscall
|
||||
def ttl_set_o(time_mu: TInt64, channel: TInt32, enabled: TBool) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
@syscall
|
||||
def ttl_set_oe(time_mu: TInt64, channel: TInt32, enabled: TBool) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
@syscall
|
||||
def ttl_set_sensitivity(time_mu: TInt64, channel: TInt32, sensitivity: TInt32) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
@syscall
|
||||
def ttl_get(channel: TInt32, time_limit_mu: TInt64) -> TInt64:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
@syscall
|
||||
def ttl_clock_set(time_mu: TInt64, channel: TInt32, ftw: TInt32) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
|
||||
class TTLOut:
|
||||
|
@ -18,14 +40,14 @@ class TTLOut:
|
|||
|
||||
@kernel
|
||||
def set_o(self, o):
|
||||
syscall("ttl_set_o", now_mu(), self.channel, o)
|
||||
ttl_set_o(now_mu(), self.channel, o)
|
||||
self.o_previous_timestamp = now_mu()
|
||||
|
||||
@kernel
|
||||
def sync(self):
|
||||
"""Busy-wait until all programmed level switches have been
|
||||
effected."""
|
||||
while syscall("rtio_get_counter") < self.o_previous_timestamp:
|
||||
while self.core.get_rtio_counter_mu() < self.o_previous_timestamp:
|
||||
pass
|
||||
|
||||
@kernel
|
||||
|
@ -83,7 +105,7 @@ class TTLInOut:
|
|||
|
||||
@kernel
|
||||
def set_oe(self, oe):
|
||||
syscall("ttl_set_oe", now_mu(), self.channel, oe)
|
||||
ttl_set_oe(now_mu(), self.channel, oe)
|
||||
|
||||
@kernel
|
||||
def output(self):
|
||||
|
@ -95,14 +117,14 @@ class TTLInOut:
|
|||
|
||||
@kernel
|
||||
def set_o(self, o):
|
||||
syscall("ttl_set_o", now_mu(), self.channel, o)
|
||||
ttl_set_o(now_mu(), self.channel, o)
|
||||
self.o_previous_timestamp = now_mu()
|
||||
|
||||
@kernel
|
||||
def sync(self):
|
||||
"""Busy-wait until all programmed level switches have been
|
||||
effected."""
|
||||
while syscall("rtio_get_counter") < self.o_previous_timestamp:
|
||||
while self.core.get_rtio_counter_mu() < self.o_previous_timestamp:
|
||||
pass
|
||||
|
||||
@kernel
|
||||
|
@ -133,7 +155,7 @@ class TTLInOut:
|
|||
|
||||
@kernel
|
||||
def _set_sensitivity(self, value):
|
||||
syscall("ttl_set_sensitivity", now_mu(), self.channel, value)
|
||||
ttl_set_sensitivity(now_mu(), self.channel, value)
|
||||
self.i_previous_timestamp = now_mu()
|
||||
|
||||
@kernel
|
||||
|
@ -189,8 +211,7 @@ class TTLInOut:
|
|||
"""Poll the RTIO input during all the previously programmed gate
|
||||
openings, and returns the number of registered events."""
|
||||
count = 0
|
||||
while syscall("ttl_get", self.channel,
|
||||
self.i_previous_timestamp) >= 0:
|
||||
while ttl_get(self.channel, self.i_previous_timestamp) >= 0:
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
@ -201,7 +222,7 @@ class TTLInOut:
|
|||
|
||||
If the gate is permanently closed, returns a negative value.
|
||||
"""
|
||||
return syscall("ttl_get", self.channel, self.i_previous_timestamp)
|
||||
return ttl_get(self.channel, self.i_previous_timestamp)
|
||||
|
||||
|
||||
class TTLClockGen:
|
||||
|
@ -254,7 +275,7 @@ class TTLClockGen:
|
|||
that are not powers of two cause jitter of one RTIO clock cycle at the
|
||||
output.
|
||||
"""
|
||||
syscall("ttl_clock_set", now_mu(), self.channel, frequency)
|
||||
ttl_clock_set(now_mu(), self.channel, frequency)
|
||||
self.previous_timestamp = now_mu()
|
||||
|
||||
@kernel
|
||||
|
@ -271,5 +292,5 @@ class TTLClockGen:
|
|||
def sync(self):
|
||||
"""Busy-wait until all programmed frequency switches and stops have
|
||||
been effected."""
|
||||
while syscall("rtio_get_counter") < self.o_previous_timestamp:
|
||||
while self.core.get_rtio_counter_mu() < self.o_previous_timestamp:
|
||||
pass
|
||||
|
|
|
@ -7,15 +7,19 @@ from collections import namedtuple
|
|||
from functools import wraps
|
||||
|
||||
|
||||
__all__ = ["int64", "round64", "kernel", "portable",
|
||||
"set_time_manager", "set_syscall_manager", "set_watchdog_factory",
|
||||
|
||||
__all__ = ["int64", "round64",
|
||||
"kernel", "portable", "syscall",
|
||||
"set_time_manager", "set_watchdog_factory",
|
||||
"ARTIQException"]
|
||||
|
||||
# global namespace for kernels
|
||||
kernel_globals = ("sequential", "parallel",
|
||||
kernel_globals = (
|
||||
"sequential", "parallel",
|
||||
"delay_mu", "now_mu", "at_mu", "delay",
|
||||
"seconds_to_mu", "mu_to_seconds",
|
||||
"syscall", "watchdog")
|
||||
"watchdog"
|
||||
)
|
||||
__all__.extend(kernel_globals)
|
||||
|
||||
|
||||
|
@ -78,11 +82,12 @@ def round64(x):
|
|||
return int64(round(x))
|
||||
|
||||
|
||||
_ARTIQEmbeddedInfo = namedtuple("_ARTIQEmbeddedInfo", "core_name function")
|
||||
|
||||
_ARTIQEmbeddedInfo = namedtuple("_ARTIQEmbeddedInfo",
|
||||
"core_name function syscall")
|
||||
|
||||
def kernel(arg):
|
||||
"""This decorator marks an object's method for execution on the core
|
||||
"""
|
||||
This decorator marks an object's method for execution on the core
|
||||
device.
|
||||
|
||||
When a decorated method is called from the Python interpreter, the ``core``
|
||||
|
@ -106,15 +111,15 @@ def kernel(arg):
|
|||
def run_on_core(self, *k_args, **k_kwargs):
|
||||
return getattr(self, arg).run(function, ((self,) + k_args), k_kwargs)
|
||||
run_on_core.artiq_embedded = _ARTIQEmbeddedInfo(
|
||||
core_name=arg, function=function)
|
||||
core_name=arg, function=function, syscall=None)
|
||||
return run_on_core
|
||||
return inner_decorator
|
||||
else:
|
||||
return kernel("core")(arg)
|
||||
|
||||
|
||||
def portable(function):
|
||||
"""This decorator marks a function for execution on the same device as its
|
||||
"""
|
||||
This decorator marks a function for execution on the same device as its
|
||||
caller.
|
||||
|
||||
In other words, a decorated function called from the interpreter on the
|
||||
|
@ -122,9 +127,31 @@ def portable(function):
|
|||
core device). A decorated function called from a kernel will be executed
|
||||
on the core device (no RPC).
|
||||
"""
|
||||
function.artiq_embedded = _ARTIQEmbeddedInfo(core_name="", function=function)
|
||||
function.artiq_embedded = \
|
||||
_ARTIQEmbeddedInfo(core_name=None, function=function, syscall=None)
|
||||
return function
|
||||
|
||||
def syscall(arg):
|
||||
"""
|
||||
This decorator marks a function as a system call. When executed on a core
|
||||
device, a C function with the provided name (or the same name as
|
||||
the Python function, if not provided) will be called. When executed on
|
||||
host, the Python function will be called as usual.
|
||||
|
||||
Every argument and the return value must be annotated with ARTIQ types.
|
||||
|
||||
Only drivers should normally define syscalls.
|
||||
"""
|
||||
if isinstance(arg, str):
|
||||
def inner_decorator(function):
|
||||
function.artiq_embedded = \
|
||||
_ARTIQEmbeddedInfo(core_name=None, function=None,
|
||||
syscall=function.__name__)
|
||||
return function
|
||||
return inner_decorator
|
||||
else:
|
||||
return syscall(arg.__name__)(arg)
|
||||
|
||||
|
||||
class _DummyTimeManager:
|
||||
def _not_implemented(self, *args, **kwargs):
|
||||
|
@ -152,22 +179,6 @@ def set_time_manager(time_manager):
|
|||
_time_manager = time_manager
|
||||
|
||||
|
||||
class _DummySyscallManager:
|
||||
def do(self, *args):
|
||||
raise NotImplementedError(
|
||||
"Attempted to interpret kernel without a syscall manager")
|
||||
|
||||
_syscall_manager = _DummySyscallManager()
|
||||
|
||||
|
||||
def set_syscall_manager(syscall_manager):
|
||||
"""Set the system call manager used for simulating the core device's
|
||||
runtime in the Python interpreter.
|
||||
"""
|
||||
global _syscall_manager
|
||||
_syscall_manager = syscall_manager
|
||||
|
||||
|
||||
class _Sequential:
|
||||
"""In a sequential block, statements are executed one after another, with
|
||||
the time increasing as one moves down the statement list."""
|
||||
|
@ -240,17 +251,6 @@ def mu_to_seconds(mu, core=None):
|
|||
return mu*core.ref_period
|
||||
|
||||
|
||||
def syscall(*args):
|
||||
"""Invokes a service of the runtime.
|
||||
|
||||
Kernels use this function to interface to the outside world: program RTIO
|
||||
events, make RPCs, etc.
|
||||
|
||||
Only drivers should normally use ``syscall``.
|
||||
"""
|
||||
return _syscall_manager.do(*args)
|
||||
|
||||
|
||||
class _DummyWatchdog:
|
||||
def __init__(self, timeout):
|
||||
pass
|
||||
|
|
Loading…
Reference in New Issue