forked from M-Labs/artiq
213 lines
6.5 KiB
Python
213 lines
6.5 KiB
Python
import os
|
|
|
|
import llvmlite_artiq.ir as ll
|
|
import llvmlite_artiq.binding as llvm
|
|
|
|
from artiq.py2llvm import base_types, fractions, lists
|
|
from artiq.language import units
|
|
|
|
|
|
llvm.initialize()
|
|
llvm.initialize_all_targets()
|
|
llvm.initialize_all_asmprinters()
|
|
|
|
_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)
|
|
|
|
# exception handling
|
|
func_type = ll.FunctionType(ll.IntType(32),
|
|
[ll.PointerType(ll.IntType(8))])
|
|
self.eh_setjmp = ll.Function(llvm_module, func_type,
|
|
"__eh_setjmp")
|
|
self.eh_setjmp.attributes.add("nounwind")
|
|
self.eh_setjmp.attributes.add("returns_twice")
|
|
|
|
func_type = ll.FunctionType(ll.PointerType(ll.IntType(8)), [])
|
|
self.eh_push = ll.Function(llvm_module, func_type, "__eh_push")
|
|
|
|
func_type = ll.FunctionType(ll.VoidType(), [ll.IntType(32)])
|
|
self.eh_pop = ll.Function(llvm_module, func_type, "__eh_pop")
|
|
|
|
func_type = ll.FunctionType(ll.IntType(32), [])
|
|
self.eh_getid = ll.Function(llvm_module, func_type, "__eh_getid")
|
|
|
|
func_type = ll.FunctionType(ll.VoidType(), [ll.IntType(32)])
|
|
self.eh_raise = ll.Function(llvm_module, func_type, "__eh_raise")
|
|
self.eh_raise.attributes.add("noreturn")
|
|
|
|
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)
|
|
|
|
def build_catch(self, builder):
|
|
jmpbuf = builder.call(self.eh_push, [])
|
|
exception_occured = builder.call(self.eh_setjmp, [jmpbuf])
|
|
return builder.icmp_signed("!=",
|
|
exception_occured,
|
|
ll.Constant(ll.IntType(32), 0))
|
|
|
|
def build_pop(self, builder, levels):
|
|
builder.call(self.eh_pop, [ll.Constant(ll.IntType(32), levels)])
|
|
|
|
def build_getid(self, builder):
|
|
return builder.call(self.eh_getid, [])
|
|
|
|
def build_raise(self, builder, eid):
|
|
builder.call(self.eh_raise, [eid])
|
|
|
|
|
|
def _debug_dump_obj(obj):
|
|
try:
|
|
env = os.environ["ARTIQ_DUMP_OBJECT"]
|
|
except KeyError:
|
|
return
|
|
|
|
for i in range(1000):
|
|
filename = "{}_{:03d}.elf".format(env, i)
|
|
try:
|
|
f = open(filename, "xb")
|
|
except FileExistsError:
|
|
pass
|
|
else:
|
|
f.write(obj)
|
|
f.close()
|
|
return
|
|
raise IOError
|
|
|
|
|
|
class Runtime(LinkInterface):
|
|
def __init__(self):
|
|
self.cpu_type = "or1k"
|
|
# allow 1ms for all initial DDS programming
|
|
self.warmup_time = 1*units.ms
|
|
|
|
def emit_object(self):
|
|
tm = llvm.Target.from_triple(self.cpu_type).create_target_machine()
|
|
obj = tm.emit_object(self.module.llvm_module_ref)
|
|
_debug_dump_obj(obj)
|
|
return obj
|
|
|
|
def __repr__(self):
|
|
return "<Runtime {}>".format(self.cpu_type)
|