forked from M-Labs/artiq
Merge branch 'master' of github.com:m-labs/artiq
This commit is contained in:
commit
79986791bc
|
@ -97,6 +97,7 @@ class Target:
|
|||
llpassmgrbuilder = llvm.create_pass_manager_builder()
|
||||
llpassmgrbuilder.opt_level = 2 # -O2
|
||||
llpassmgrbuilder.size_level = 1 # -Os
|
||||
llpassmgrbuilder.inlining_threshold = 75 # -Os threshold
|
||||
|
||||
llpassmgr = llvm.create_module_pass_manager()
|
||||
llpassmgrbuilder.populate(llpassmgr)
|
||||
|
@ -147,6 +148,9 @@ class Target:
|
|||
return results["output"].read()
|
||||
|
||||
def symbolize(self, library, addresses):
|
||||
if addresses == []:
|
||||
return []
|
||||
|
||||
# Addresses point one instruction past the jump; offset them back by 1.
|
||||
offset_addresses = [hex(addr - 1) for addr in addresses]
|
||||
with RunTool([self.triple + "-addr2line", "--functions", "--inlines",
|
||||
|
|
|
@ -320,7 +320,8 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
|||
return self.append(ir.Closure(func, self.current_env))
|
||||
|
||||
def visit_FunctionDefT(self, node, in_class=None):
|
||||
func = self.visit_function(node, is_lambda=False, is_internal=len(self.name) > 2)
|
||||
func = self.visit_function(node, is_lambda=False,
|
||||
is_internal=len(self.name) > 0 or '.' in node.name)
|
||||
if in_class is None:
|
||||
self._set_local(node.name, func)
|
||||
else:
|
||||
|
|
|
@ -176,6 +176,7 @@ class LLVMIRGenerator:
|
|||
self.llobject_map = {}
|
||||
self.phis = []
|
||||
self.debug_info_emitter = DebugInfoEmitter(self.llmodule)
|
||||
self.empty_metadata = self.llmodule.add_metadata([])
|
||||
|
||||
def needs_sret(self, lltyp, may_be_large=True):
|
||||
if isinstance(lltyp, ll.VoidType):
|
||||
|
@ -190,8 +191,13 @@ class LLVMIRGenerator:
|
|||
and len(lltyp.elements) <= 2:
|
||||
return not any([self.needs_sret(elt, may_be_large=False) for elt in lltyp.elements])
|
||||
else:
|
||||
assert isinstance(lltyp, ll.Type)
|
||||
return True
|
||||
|
||||
def has_sret(self, functy):
|
||||
llretty = self.llty_of_type(functy.ret, for_return=True)
|
||||
return self.needs_sret(llretty)
|
||||
|
||||
def llty_of_type(self, typ, bare=False, for_return=False):
|
||||
typ = typ.find()
|
||||
if types.is_tuple(typ):
|
||||
|
@ -218,22 +224,14 @@ class LLVMIRGenerator:
|
|||
for arg in typ.optargs],
|
||||
return_type=llretty)
|
||||
|
||||
# TODO: actually mark the first argument as sret (also noalias nocapture).
|
||||
# llvmlite currently does not have support for this;
|
||||
# https://github.com/numba/llvmlite/issues/91.
|
||||
if sretarg:
|
||||
llty.__has_sret = True
|
||||
else:
|
||||
llty.__has_sret = False
|
||||
|
||||
if bare:
|
||||
return llty
|
||||
else:
|
||||
return ll.LiteralStructType([envarg, llty.as_pointer()])
|
||||
elif types.is_method(typ):
|
||||
llfuncty = self.llty_of_type(types.get_method_function(typ))
|
||||
llfunty = self.llty_of_type(types.get_method_function(typ))
|
||||
llselfty = self.llty_of_type(types.get_method_self(typ))
|
||||
return ll.LiteralStructType([llfuncty, llselfty])
|
||||
return ll.LiteralStructType([llfunty, llselfty])
|
||||
elif builtins.is_none(typ):
|
||||
if for_return:
|
||||
return llvoid
|
||||
|
@ -399,8 +397,13 @@ class LLVMIRGenerator:
|
|||
elif isinstance(value, ir.Function):
|
||||
llfun = self.llmodule.get_global(value.name)
|
||||
if llfun is None:
|
||||
llfun = ll.Function(self.llmodule, self.llty_of_type(value.type, bare=True),
|
||||
value.name)
|
||||
llfunty = self.llty_of_type(value.type, bare=True)
|
||||
llfun = ll.Function(self.llmodule, llfunty, value.name)
|
||||
|
||||
llretty = self.llty_of_type(value.type.ret, for_return=True)
|
||||
if self.needs_sret(llretty):
|
||||
llfun.args[0].add_attribute('sret')
|
||||
|
||||
return llfun
|
||||
else:
|
||||
assert False
|
||||
|
@ -515,11 +518,7 @@ class LLVMIRGenerator:
|
|||
|
||||
def process_function(self, func):
|
||||
try:
|
||||
self.llfunction = self.llmodule.get_global(func.name)
|
||||
|
||||
if self.llfunction is None:
|
||||
llfunty = self.llty_of_type(func.type, bare=True)
|
||||
self.llfunction = ll.Function(self.llmodule, llfunty, func.name)
|
||||
self.llfunction = self.map(func)
|
||||
|
||||
if func.is_internal:
|
||||
self.llfunction.linkage = 'internal'
|
||||
|
@ -532,7 +531,7 @@ class LLVMIRGenerator:
|
|||
disubprogram = self.debug_info_emitter.emit_subprogram(func, self.llfunction)
|
||||
|
||||
# First, map arguments.
|
||||
if self.llfunction.type.pointee.__has_sret:
|
||||
if self.has_sret(func.type):
|
||||
llactualargs = self.llfunction.args[1:]
|
||||
else:
|
||||
llactualargs = self.llfunction.args
|
||||
|
@ -615,7 +614,8 @@ class LLVMIRGenerator:
|
|||
llalloc = self.llbuilder.alloca(self.llty_of_type(insn.type, bare=True))
|
||||
for index, operand in enumerate(insn.operands):
|
||||
lloperand = self.map(operand)
|
||||
llfieldptr = self.llbuilder.gep(llalloc, [self.llindex(0), self.llindex(index)])
|
||||
llfieldptr = self.llbuilder.gep(llalloc, [self.llindex(0), self.llindex(index)],
|
||||
inbounds=True)
|
||||
self.llbuilder.store(lloperand, llfieldptr)
|
||||
return llalloc
|
||||
|
||||
|
@ -623,11 +623,14 @@ class LLVMIRGenerator:
|
|||
if var_name in env_ty.params and (var_type is None or
|
||||
env_ty.params[var_name] == var_type):
|
||||
var_index = list(env_ty.params.keys()).index(var_name)
|
||||
return self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(var_index)])
|
||||
return self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(var_index)],
|
||||
inbounds=True)
|
||||
else:
|
||||
outer_index = list(env_ty.params.keys()).index("$outer")
|
||||
llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)])
|
||||
llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)],
|
||||
inbounds=True)
|
||||
llouterenv = self.llbuilder.load(llptr)
|
||||
llouterenv.metadata['invariant.load'] = self.empty_metadata
|
||||
return self.llptr_to_var(llouterenv, env_ty.params["$outer"], var_name)
|
||||
|
||||
def process_GetLocal(self, insn):
|
||||
|
@ -638,7 +641,9 @@ class LLVMIRGenerator:
|
|||
def process_GetConstructor(self, insn):
|
||||
env = insn.environment()
|
||||
llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name, insn.type)
|
||||
return self.llbuilder.load(llptr)
|
||||
llconstr = self.llbuilder.load(llptr)
|
||||
llconstr.metadata['invariant.load'] = self.empty_metadata
|
||||
return llconstr
|
||||
|
||||
def process_SetLocal(self, insn):
|
||||
env = insn.environment()
|
||||
|
@ -667,14 +672,14 @@ class LLVMIRGenerator:
|
|||
else:
|
||||
llptr = self.llbuilder.gep(self.map(insn.object()),
|
||||
[self.llindex(0), self.llindex(self.attr_index(insn))],
|
||||
name=insn.name)
|
||||
inbounds=True, name=insn.name)
|
||||
return self.llbuilder.load(llptr)
|
||||
|
||||
def process_SetAttr(self, insn):
|
||||
assert builtins.is_allocated(insn.object().type)
|
||||
llptr = self.llbuilder.gep(self.map(insn.object()),
|
||||
[self.llindex(0), self.llindex(self.attr_index(insn))],
|
||||
name=insn.name)
|
||||
inbounds=True, name=insn.name)
|
||||
return self.llbuilder.store(self.map(insn.value()), llptr)
|
||||
|
||||
def process_GetElem(self, insn):
|
||||
|
@ -872,8 +877,10 @@ class LLVMIRGenerator:
|
|||
def get_outer(llenv, env_ty):
|
||||
if "$outer" in env_ty.params:
|
||||
outer_index = list(env_ty.params.keys()).index("$outer")
|
||||
llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)])
|
||||
llptr = self.llbuilder.gep(llenv, [self.llindex(0), self.llindex(outer_index)],
|
||||
inbounds=True)
|
||||
llouterenv = self.llbuilder.load(llptr)
|
||||
llouterenv.metadata['invariant.load'] = self.empty_metadata
|
||||
return self.llptr_to_var(llouterenv, env_ty.params["$outer"], var_name)
|
||||
else:
|
||||
return llenv
|
||||
|
@ -920,21 +927,46 @@ class LLVMIRGenerator:
|
|||
return llvalue
|
||||
|
||||
def _prepare_closure_call(self, insn):
|
||||
llclosure = self.map(insn.target_function())
|
||||
llargs = [self.map(arg) for arg in insn.arguments()]
|
||||
llclosure = self.map(insn.target_function())
|
||||
llenv = self.llbuilder.extract_value(llclosure, 0)
|
||||
llfun = self.llbuilder.extract_value(llclosure, 1)
|
||||
if insn.static_target_function is None:
|
||||
llfun = self.llbuilder.extract_value(llclosure, 1)
|
||||
else:
|
||||
llfun = self.map(insn.static_target_function)
|
||||
return llfun, [llenv] + list(llargs)
|
||||
|
||||
def _prepare_ffi_call(self, insn):
|
||||
llargs = [self.map(arg) for arg in insn.arguments()]
|
||||
llargs = []
|
||||
byvals = []
|
||||
for i, arg in enumerate(insn.arguments()):
|
||||
llarg = self.map(arg)
|
||||
if isinstance(llarg.type, (ll.LiteralStructType, ll.IdentifiedStructType)):
|
||||
llslot = self.llbuilder.alloca(llarg.type)
|
||||
self.llbuilder.store(llarg, llslot)
|
||||
llargs.append(llslot)
|
||||
byvals.append(i)
|
||||
else:
|
||||
llargs.append(llarg)
|
||||
|
||||
llfunname = insn.target_function().type.name
|
||||
llfun = self.llmodule.get_global(llfunname)
|
||||
if llfun is None:
|
||||
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)
|
||||
llretty = self.llty_of_type(insn.type, for_return=True)
|
||||
if self.needs_sret(llretty):
|
||||
llfunty = ll.FunctionType(llvoid, [llretty.as_pointer()] +
|
||||
[llarg.type for llarg in llargs])
|
||||
else:
|
||||
llfunty = ll.FunctionType(llretty, [llarg.type for llarg in llargs])
|
||||
|
||||
llfun = ll.Function(self.llmodule, llfunty,
|
||||
insn.target_function().type.name)
|
||||
if self.needs_sret(llretty):
|
||||
llfun.args[0].add_attribute('sret')
|
||||
byvals = [i + 1 for i in byvals]
|
||||
for i in byvals:
|
||||
llfun.args[i].add_attribute('byval')
|
||||
|
||||
return llfun, list(llargs)
|
||||
|
||||
# See session.c:{send,receive}_rpc_value and comm_generic.py:_{send,receive}_rpc_value.
|
||||
|
@ -1078,24 +1110,22 @@ class LLVMIRGenerator:
|
|||
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)
|
||||
|
||||
if llfun.type.pointee.__has_sret:
|
||||
llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [])
|
||||
if self.has_sret(insn.target_function().type):
|
||||
llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [])
|
||||
|
||||
llresultslot = self.llbuilder.alloca(llfun.type.pointee.args[0].pointee)
|
||||
self.llbuilder.call(llfun, [llresultslot] + llargs)
|
||||
llresult = self.llbuilder.load(llresultslot)
|
||||
llresultslot = self.llbuilder.alloca(llfun.type.pointee.args[0].pointee)
|
||||
self.llbuilder.call(llfun, [llresultslot] + llargs)
|
||||
llresult = self.llbuilder.load(llresultslot)
|
||||
|
||||
self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr])
|
||||
self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr])
|
||||
|
||||
return llresult
|
||||
else:
|
||||
return self.llbuilder.call(llfun, llargs,
|
||||
name=insn.name)
|
||||
return llresult
|
||||
else:
|
||||
return self.llbuilder.call(llfun, llargs,
|
||||
name=insn.name)
|
||||
|
||||
def process_Invoke(self, insn):
|
||||
llnormalblock = self.map(insn.normal_target())
|
||||
|
@ -1209,11 +1239,12 @@ class LLVMIRGenerator:
|
|||
if builtins.is_none(insn.value().type):
|
||||
return self.llbuilder.ret_void()
|
||||
else:
|
||||
if self.llfunction.type.pointee.__has_sret:
|
||||
self.llbuilder.store(self.map(insn.value()), self.llfunction.args[0])
|
||||
llvalue = self.map(insn.value())
|
||||
if self.needs_sret(llvalue.type):
|
||||
self.llbuilder.store(llvalue, self.llfunction.args[0])
|
||||
return self.llbuilder.ret_void()
|
||||
else:
|
||||
return self.llbuilder.ret(self.map(insn.value()))
|
||||
return self.llbuilder.ret(llvalue)
|
||||
|
||||
def process_Unreachable(self, insn):
|
||||
return self.llbuilder.unreachable()
|
||||
|
@ -1250,7 +1281,8 @@ class LLVMIRGenerator:
|
|||
cleanup=True)
|
||||
llrawexn = self.llbuilder.extract_value(lllandingpad, 1)
|
||||
llexn = self.llbuilder.bitcast(llrawexn, self.llty_of_type(insn.type))
|
||||
llexnnameptr = self.llbuilder.gep(llexn, [self.llindex(0), self.llindex(0)])
|
||||
llexnnameptr = self.llbuilder.gep(llexn, [self.llindex(0), self.llindex(0)],
|
||||
inbounds=True)
|
||||
llexnname = self.llbuilder.load(llexnnameptr)
|
||||
|
||||
for target, typ in insn.clauses():
|
||||
|
|
|
@ -610,8 +610,13 @@ 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_c_function(typ, name=None):
|
||||
typ = typ.find()
|
||||
if name is None:
|
||||
return isinstance(typ, TCFunction)
|
||||
else:
|
||||
return isinstance(typ, TCFunction) and \
|
||||
typ.name == name
|
||||
|
||||
def is_builtin(typ, name=None):
|
||||
typ = typ.find()
|
||||
|
|
|
@ -87,7 +87,13 @@ class RegionOf(algorithm.Visitor):
|
|||
return None
|
||||
|
||||
visit_BinOpT = visit_sometimes_allocating
|
||||
visit_CallT = visit_sometimes_allocating
|
||||
|
||||
def visit_CallT(self, node):
|
||||
if types.is_c_function(node.func.type, "cache_get"):
|
||||
# The cache is borrow checked dynamically
|
||||
return None
|
||||
else:
|
||||
self.visit_sometimes_allocating(node)
|
||||
|
||||
# Value lives as long as the object/container, if it's mutable,
|
||||
# or else forever
|
||||
|
|
|
@ -512,7 +512,7 @@ class CommGeneric:
|
|||
assert exception.id != 0
|
||||
python_exn_type = object_map.retrieve(exception.id)
|
||||
|
||||
python_exn = python_exn_type(message)
|
||||
python_exn = python_exn_type(message.format(*params))
|
||||
python_exn.artiq_exception = exception
|
||||
raise python_exn
|
||||
|
||||
|
|
|
@ -37,6 +37,14 @@ class CompileError(Exception):
|
|||
def rtio_get_counter() -> TInt64:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
@syscall
|
||||
def cache_get(key: TStr) -> TList(TInt32):
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
@syscall
|
||||
def cache_put(key: TStr, value: TList(TInt32)) -> TNone:
|
||||
raise NotImplementedError("syscall not simulated")
|
||||
|
||||
class Core:
|
||||
"""Core device driver.
|
||||
|
||||
|
@ -108,3 +116,11 @@ class Core:
|
|||
min_now = rtio_get_counter() + 125000
|
||||
if now_mu() < min_now:
|
||||
at_mu(min_now)
|
||||
|
||||
@kernel
|
||||
def get_cache(self, key):
|
||||
return cache_get(key)
|
||||
|
||||
@kernel
|
||||
def put_cache(self, key, value):
|
||||
return cache_put(key, value)
|
||||
|
|
|
@ -11,6 +11,10 @@ class InternalError(Exception):
|
|||
"""Raised when the runtime encounters an internal error condition."""
|
||||
|
||||
|
||||
class CacheError(Exception):
|
||||
"""Raised when putting a value into a cache row would violate memory safety."""
|
||||
|
||||
|
||||
class RTIOUnderflow(Exception):
|
||||
"""Raised when the CPU fails to submit a RTIO event early enough
|
||||
(with respect to the event's timestamp).
|
||||
|
|
|
@ -7,7 +7,7 @@ from misoc.integration.soc_core import mem_decoder
|
|||
|
||||
class KernelCPU(Module):
|
||||
def __init__(self, platform,
|
||||
exec_address=0x40800000,
|
||||
exec_address=0x42000000,
|
||||
main_mem_origin=0x40000000,
|
||||
l2_size=8192):
|
||||
self._reset = CSRStorage(reset=1)
|
||||
|
|
|
@ -9,7 +9,8 @@ OBJECTS := isr.o clock.o rtiocrg.o flash_storage.o mailbox.o \
|
|||
OBJECTS_KSUPPORT := ksupport.o artiq_personality.o mailbox.o \
|
||||
bridge.o rtio.o ttl.o dds.o
|
||||
|
||||
CFLAGS += -I$(MISOC_DIRECTORY)/software/include/dyld \
|
||||
CFLAGS += -I$(LIBALLOC_DIRECTORY) \
|
||||
-I$(MISOC_DIRECTORY)/software/include/dyld \
|
||||
-I$(LIBDYLD_DIRECTORY)/include \
|
||||
-I$(LIBUNWIND_DIRECTORY) \
|
||||
-I$(LIBUNWIND_DIRECTORY)/../unwinder/include \
|
||||
|
@ -31,10 +32,11 @@ runtime.elf: $(OBJECTS)
|
|||
-N -o $@ \
|
||||
../libbase/crt0-$(CPU).o \
|
||||
$(OBJECTS) \
|
||||
-L../libbase \
|
||||
-L../libcompiler_rt \
|
||||
-L../libbase \
|
||||
-L../liballoc \
|
||||
-L../liblwip \
|
||||
-lbase -lcompiler_rt -llwip
|
||||
-lbase -lcompiler_rt -lalloc -llwip
|
||||
@chmod -x $@
|
||||
|
||||
ksupport.elf: $(OBJECTS_KSUPPORT)
|
||||
|
@ -48,7 +50,7 @@ ksupport.elf: $(OBJECTS_KSUPPORT)
|
|||
-L../libcompiler_rt \
|
||||
-L../libunwind \
|
||||
-L../libdyld \
|
||||
-lbase -lcompiler_rt -lunwind -ldyld
|
||||
-lbase -lcompiler_rt -ldyld -lunwind
|
||||
@chmod -x $@
|
||||
|
||||
ksupport_data.o: ksupport.elf
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
#include "artiq_personality.h"
|
||||
|
||||
#define KERNELCPU_EXEC_ADDRESS 0x40800000
|
||||
#define KERNELCPU_PAYLOAD_ADDRESS 0x40820000
|
||||
#define KERNELCPU_EXEC_ADDRESS 0x42000000
|
||||
#define KERNELCPU_PAYLOAD_ADDRESS 0x42020000
|
||||
#define KERNELCPU_LAST_ADDRESS (0x4fffffff - 1024*1024)
|
||||
#define KSUPPORT_HEADER_SIZE 0x80
|
||||
|
||||
|
|
|
@ -122,6 +122,9 @@ static const struct symbol runtime_exports[] = {
|
|||
{"dds_batch_exit", &dds_batch_exit},
|
||||
{"dds_set", &dds_set},
|
||||
|
||||
{"cache_get", &cache_get},
|
||||
{"cache_put", &cache_put},
|
||||
|
||||
/* end */
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
@ -222,7 +225,7 @@ 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 {1}, EA {2}",
|
||||
"Hardware exception {0} at PC 0x{1:08x}, EA 0x{2:08x}",
|
||||
vect, pc, ea);
|
||||
}
|
||||
|
||||
|
@ -444,6 +447,50 @@ void attribute_writeback(void *utypes) {
|
|||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
log("Malformed MESSAGE_TYPE_CACHE_GET_REQUEST reply type %d",
|
||||
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) {
|
||||
log("Malformed MESSAGE_TYPE_CACHE_PUT_REQUEST reply type %d",
|
||||
reply->type);
|
||||
while(1);
|
||||
}
|
||||
|
||||
if(!reply->succeeded) {
|
||||
artiq_raise_from_c("CacheError",
|
||||
"cannot put into a busy cache row",
|
||||
0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void lognonl(const char *fmt, ...)
|
||||
{
|
||||
struct msg_log request;
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
#ifndef __KSTARTUP_H
|
||||
#define __KSTARTUP_H
|
||||
|
||||
struct artiq_list {
|
||||
int32_t length;
|
||||
int32_t *elements;
|
||||
};
|
||||
|
||||
long long int now_init(void);
|
||||
void now_save(long long int now);
|
||||
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 lognonl(const char *fmt, ...);
|
||||
void log(const char *fmt, ...);
|
||||
|
||||
|
|
|
@ -3,11 +3,12 @@ ENTRY(_start)
|
|||
|
||||
INCLUDE generated/regions.ld
|
||||
|
||||
/* First 8M of main memory are reserved for runtime code/data
|
||||
* then comes kernel memory. First 128K of kernel memory are for support code.
|
||||
/* First 32M of main memory are reserved for runtime
|
||||
* code/data/heap, then comes kernel memory.
|
||||
* First 128K of kernel memory are for support code.
|
||||
*/
|
||||
MEMORY {
|
||||
ksupport (RWX) : ORIGIN = 0x40800000, LENGTH = 0x20000
|
||||
ksupport (RWX) : ORIGIN = 0x42000000, LENGTH = 0x20000
|
||||
}
|
||||
|
||||
/* On AMP systems, kernel stack is at the end of main RAM,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <alloc.h>
|
||||
#include <irq.h>
|
||||
#include <uart.h>
|
||||
#include <console.h>
|
||||
|
@ -263,6 +264,8 @@ static int check_test_mode(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
extern void _fheap, _eheap;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
irq_setmask(0);
|
||||
|
@ -271,6 +274,7 @@ int main(void)
|
|||
|
||||
puts("ARTIQ runtime built "__DATE__" "__TIME__"\n");
|
||||
|
||||
alloc_give(&_fheap, &_eheap - &_fheap);
|
||||
clock_init();
|
||||
rtiocrg_init();
|
||||
puts("Press 't' to enter test mode...");
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum {
|
||||
MESSAGE_TYPE_LOAD_REPLY,
|
||||
|
@ -18,6 +19,10 @@ enum {
|
|||
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,
|
||||
|
@ -105,6 +110,29 @@ struct msg_rpc_batch {
|
|||
void *ptr;
|
||||
};
|
||||
|
||||
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;
|
||||
|
|
|
@ -7,7 +7,7 @@ INCLUDE generated/regions.ld
|
|||
* ld does not allow this expression here.
|
||||
*/
|
||||
MEMORY {
|
||||
runtime : ORIGIN = 0x40000000, LENGTH = 0x800000 /* 8M */
|
||||
runtime : ORIGIN = 0x40000000, LENGTH = 0x2000000 /* 32M */
|
||||
}
|
||||
|
||||
/* Kernel memory space start right after the runtime,
|
||||
|
@ -65,5 +65,7 @@ SECTIONS
|
|||
*(.eh_frame)
|
||||
}
|
||||
|
||||
_heapstart = .;
|
||||
_fheap = .;
|
||||
. += 0x1800000;
|
||||
_eheap = .;
|
||||
}
|
||||
|
|
|
@ -908,6 +908,16 @@ static int send_rpc_request(int service, const char *tag, va_list args)
|
|||
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)
|
||||
{
|
||||
|
@ -930,9 +940,12 @@ static int process_kmsg(struct msg_base *umsg)
|
|||
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;
|
||||
mailbox_acknowledge();
|
||||
|
||||
break;
|
||||
|
||||
case MESSAGE_TYPE_EXCEPTION: {
|
||||
|
@ -984,6 +997,67 @@ static int process_kmsg(struct msg_base *umsg)
|
|||
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)) {
|
||||
free(iter->elements);
|
||||
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) {
|
||||
if(request->length != 0) {
|
||||
row->length = request->length;
|
||||
row->elements = calloc(row->length, sizeof(int32_t));
|
||||
memcpy(row->elements, request->elements,
|
||||
sizeof(int32_t) * row->length);
|
||||
}
|
||||
|
||||
reply.succeeded = 1;
|
||||
} else {
|
||||
reply.succeeded = 0;
|
||||
}
|
||||
|
||||
mailbox_send(&reply);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
log("Received invalid message type %d from kernel CPU",
|
||||
umsg->type);
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
from artiq.language import *
|
||||
from artiq.coredevice.exceptions import *
|
||||
from artiq.test.hardware_testbench import ExperimentCase
|
||||
|
||||
|
||||
class _Cache(EnvExperiment):
|
||||
def build(self):
|
||||
self.setattr_device("core")
|
||||
self.print = lambda x: print(x)
|
||||
|
||||
@kernel
|
||||
def get(self, key):
|
||||
return self.core.get_cache(key)
|
||||
|
||||
@kernel
|
||||
def put(self, key, value):
|
||||
self.core.put_cache(key, value)
|
||||
|
||||
@kernel
|
||||
def get_put(self, key, value):
|
||||
self.get(key)
|
||||
self.put(key, value)
|
||||
|
||||
class CacheTest(ExperimentCase):
|
||||
def test_get_empty(self):
|
||||
exp = self.create(_Cache)
|
||||
self.assertEqual(exp.get("x1"), [])
|
||||
|
||||
def test_put_get(self):
|
||||
exp = self.create(_Cache)
|
||||
exp.put("x2", [1, 2, 3])
|
||||
self.assertEqual(exp.get("x2"), [1, 2, 3])
|
||||
|
||||
def test_replace(self):
|
||||
exp = self.create(_Cache)
|
||||
exp.put("x3", [1, 2, 3])
|
||||
exp.put("x3", [1, 2, 3, 4, 5])
|
||||
self.assertEqual(exp.get("x3"), [1, 2, 3, 4, 5])
|
||||
|
||||
def test_borrow(self):
|
||||
exp = self.create(_Cache)
|
||||
exp.put("x4", [1, 2, 3])
|
||||
with self.assertRaises(CacheError):
|
||||
exp.get_put("x4", [])
|
Loading…
Reference in New Issue