satman: allow subkernels start subkernels

This commit is contained in:
mwojcik 2023-11-23 18:25:07 +08:00 committed by Sébastien Bourdeauducq
parent 9bc66e5c14
commit 4956fac861
21 changed files with 392 additions and 220 deletions

View File

@ -48,7 +48,7 @@ class SpecializedFunction:
class EmbeddingMap: class EmbeddingMap:
def __init__(self): def __init__(self, subkernels={}):
self.object_current_key = 0 self.object_current_key = 0
self.object_forward_map = {} self.object_forward_map = {}
self.object_reverse_map = {} self.object_reverse_map = {}
@ -64,6 +64,13 @@ class EmbeddingMap:
self.function_map = {} self.function_map = {}
self.str_forward_map = {} self.str_forward_map = {}
self.str_reverse_map = {} self.str_reverse_map = {}
# subkernels: dict of ID: function, just like object_forward_map
# allow the embedding map to be aware of subkernels from other kernels
for key, obj_ref in subkernels.items():
self.object_forward_map[key] = obj_ref
obj_id = id(obj_ref)
self.object_reverse_map[obj_id] = key
self.preallocate_runtime_exception_names(["RuntimeError", self.preallocate_runtime_exception_names(["RuntimeError",
"RTIOUnderflow", "RTIOUnderflow",
@ -165,6 +172,11 @@ class EmbeddingMap:
return self.object_reverse_map[obj_id] return self.object_reverse_map[obj_id]
self.object_current_key += 1 self.object_current_key += 1
while self.object_forward_map.get(self.object_current_key):
# make sure there's no collisions with previously inserted subkernels
# their identifiers must be consistent between kernels/subkernels
self.object_current_key += 1
self.object_forward_map[self.object_current_key] = obj_ref self.object_forward_map[self.object_current_key] = obj_ref
self.object_reverse_map[obj_id] = self.object_current_key self.object_reverse_map[obj_id] = self.object_current_key
return self.object_current_key return self.object_current_key
@ -200,10 +212,6 @@ class EmbeddingMap:
self.object_forward_map.values() self.object_forward_map.values()
)) ))
def has_rpc_or_subkernel(self):
return any(filter(lambda x: inspect.isfunction(x) or inspect.ismethod(x),
self.object_forward_map.values()))
class ASTSynthesizer: class ASTSynthesizer:
def __init__(self, embedding_map, value_map, quote_function=None, expanded_from=None): def __init__(self, embedding_map, value_map, quote_function=None, expanded_from=None):
@ -794,7 +802,7 @@ class TypedtreeHasher(algorithm.Visitor):
return hash(tuple(freeze(getattr(node, field_name)) for field_name in fields)) return hash(tuple(freeze(getattr(node, field_name)) for field_name in fields))
class Stitcher: class Stitcher:
def __init__(self, core, dmgr, engine=None, print_as_rpc=True, destination=0, subkernel_arg_types=[]): def __init__(self, core, dmgr, engine=None, print_as_rpc=True, destination=0, subkernel_arg_types=[], subkernels={}):
self.core = core self.core = core
self.dmgr = dmgr self.dmgr = dmgr
if engine is None: if engine is None:
@ -816,7 +824,7 @@ class Stitcher:
self.functions = {} self.functions = {}
self.embedding_map = EmbeddingMap() self.embedding_map = EmbeddingMap(subkernels)
self.value_map = defaultdict(lambda: []) self.value_map = defaultdict(lambda: [])
self.definitely_changed = False self.definitely_changed = False

View File

@ -2557,7 +2557,8 @@ class ARTIQIRGenerator(algorithm.Visitor):
if types.is_method(fn): if types.is_method(fn):
fn = types.get_method_function(fn) fn = types.get_method_function(fn)
sid = ir.Constant(fn.sid, builtins.TInt32()) sid = ir.Constant(fn.sid, builtins.TInt32())
return self.append(ir.Builtin("subkernel_preload", [sid], builtins.TNone())) dest = ir.Constant(fn.destination, builtins.TInt32())
return self.append(ir.Builtin("subkernel_preload", [sid, dest], builtins.TNone()))
elif types.is_exn_constructor(typ): elif types.is_exn_constructor(typ):
return self.alloc_exn(node.type, *[self.visit(arg_node) for arg_node in node.args]) return self.alloc_exn(node.type, *[self.visit(arg_node) for arg_node in node.args])
elif types.is_constructor(typ): elif types.is_constructor(typ):

View File

@ -399,9 +399,9 @@ class LLVMIRGenerator:
llty = ll.FunctionType(lli32, [llptr]) llty = ll.FunctionType(lli32, [llptr])
elif name == "subkernel_send_message": elif name == "subkernel_send_message":
llty = ll.FunctionType(llvoid, [lli32, lli8, llsliceptr, llptrptr]) llty = ll.FunctionType(llvoid, [lli32, lli1, lli8, lli8, llsliceptr, llptrptr])
elif name == "subkernel_load_run": elif name == "subkernel_load_run":
llty = ll.FunctionType(llvoid, [lli32, lli1]) llty = ll.FunctionType(llvoid, [lli32, lli8, lli1])
elif name == "subkernel_await_finish": elif name == "subkernel_await_finish":
llty = ll.FunctionType(llvoid, [lli32, lli64]) llty = ll.FunctionType(llvoid, [lli32, lli64])
elif name == "subkernel_await_message": elif name == "subkernel_await_message":
@ -1417,7 +1417,8 @@ class LLVMIRGenerator:
return self._build_rpc_recv(insn.type, llstackptr) return self._build_rpc_recv(insn.type, llstackptr)
elif insn.op == "subkernel_preload": elif insn.op == "subkernel_preload":
llsid = self.map(insn.operands[0]) llsid = self.map(insn.operands[0])
return self.llbuilder.call(self.llbuiltin("subkernel_load_run"), [llsid, ll.Constant(lli1, 0)], lldest = ll.Constant(lli8, insn.operands[1].value)
return self.llbuilder.call(self.llbuiltin("subkernel_load_run"), [llsid, lldest, ll.Constant(lli1, 0)],
name="subkernel.preload") name="subkernel.preload")
else: else:
assert False assert False
@ -1660,6 +1661,7 @@ class LLVMIRGenerator:
def _build_subkernel_call(self, fun_loc, fun_type, args): def _build_subkernel_call(self, fun_loc, fun_type, args):
llsid = ll.Constant(lli32, fun_type.sid) llsid = ll.Constant(lli32, fun_type.sid)
lldest = ll.Constant(lli8, fun_type.destination)
tag = b"" tag = b""
for arg in args: for arg in args:
@ -1678,7 +1680,7 @@ class LLVMIRGenerator:
tag += b":" tag += b":"
# run the kernel first # run the kernel first
self.llbuilder.call(self.llbuiltin("subkernel_load_run"), [llsid, ll.Constant(lli1, 1)]) self.llbuilder.call(self.llbuiltin("subkernel_load_run"), [llsid, lldest, ll.Constant(lli1, 1)])
# arg sent in the same vein as RPC # arg sent in the same vein as RPC
llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [], llstackptr = self.llbuilder.call(self.llbuiltin("llvm.stacksave"), [],
@ -1708,8 +1710,10 @@ class LLVMIRGenerator:
llargcount = ll.Constant(lli8, len(args)) llargcount = ll.Constant(lli8, len(args))
llisreturn = ll.Constant(lli1, False)
self.llbuilder.call(self.llbuiltin("subkernel_send_message"), self.llbuilder.call(self.llbuiltin("subkernel_send_message"),
[llsid, llargcount, lltagptr, llargs]) [llsid, llisreturn, lldest, llargcount, lltagptr, llargs])
self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr]) self.llbuilder.call(self.llbuiltin("llvm.stackrestore"), [llstackptr])
return llsid return llsid
@ -1746,10 +1750,12 @@ class LLVMIRGenerator:
llretslot = self.llbuilder.bitcast(llretslot, llptr) llretslot = self.llbuilder.bitcast(llretslot, llptr)
self.llbuilder.store(llretslot, llrets) self.llbuilder.store(llretslot, llrets)
llsid = ll.Constant(lli32, 0) # return goes back to master, sid is ignored llsid = ll.Constant(lli32, 0) # return goes back to the caller, sid is ignored
lltagcount = ll.Constant(lli8, 1) # only one thing is returned lltagcount = ll.Constant(lli8, 1) # only one thing is returned
llisreturn = ll.Constant(lli1, True) # it's a return, so destination is ignored
lldest = ll.Constant(lli8, 0)
self.llbuilder.call(self.llbuiltin("subkernel_send_message"), self.llbuilder.call(self.llbuiltin("subkernel_send_message"),
[llsid, lltagcount, lltagptr, llrets]) [llsid, llisreturn, lldest, lltagcount, lltagptr, llrets])
def process_Call(self, insn): def process_Call(self, insn):
functiontyp = insn.target_function().type functiontyp = insn.target_function().type

View File

@ -120,13 +120,15 @@ class Core:
def compile(self, function, args, kwargs, set_result=None, def compile(self, function, args, kwargs, set_result=None,
attribute_writeback=True, print_as_rpc=True, attribute_writeback=True, print_as_rpc=True,
target=None, destination=0, subkernel_arg_types=[]): target=None, destination=0, subkernel_arg_types=[],
subkernels={}):
try: try:
engine = _DiagnosticEngine(all_errors_are_fatal=True) engine = _DiagnosticEngine(all_errors_are_fatal=True)
stitcher = Stitcher(engine=engine, core=self, dmgr=self.dmgr, stitcher = Stitcher(engine=engine, core=self, dmgr=self.dmgr,
print_as_rpc=print_as_rpc, print_as_rpc=print_as_rpc,
destination=destination, subkernel_arg_types=subkernel_arg_types) destination=destination, subkernel_arg_types=subkernel_arg_types,
subkernels=subkernels)
stitcher.stitch_call(function, args, kwargs, set_result) stitcher.stitch_call(function, args, kwargs, set_result)
stitcher.finalize() stitcher.finalize()
@ -165,7 +167,7 @@ class Core:
self._run_compiled(kernel_library, embedding_map, symbolizer, demangler) self._run_compiled(kernel_library, embedding_map, symbolizer, demangler)
return result return result
def compile_subkernel(self, sid, subkernel_fn, embedding_map, args, subkernel_arg_types): def compile_subkernel(self, sid, subkernel_fn, embedding_map, args, subkernel_arg_types, subkernels):
# pass self to subkernels (if applicable) # pass self to subkernels (if applicable)
# assuming the first argument is self # assuming the first argument is self
subkernel_args = getfullargspec(subkernel_fn.artiq_embedded.function) subkernel_args = getfullargspec(subkernel_fn.artiq_embedded.function)
@ -179,17 +181,30 @@ class Core:
object_map, kernel_library, _, _, _ = \ object_map, kernel_library, _, _, _ = \
self.compile(subkernel_fn, self_arg, {}, attribute_writeback=False, self.compile(subkernel_fn, self_arg, {}, attribute_writeback=False,
print_as_rpc=False, target=target, destination=destination, print_as_rpc=False, target=target, destination=destination,
subkernel_arg_types=subkernel_arg_types.get(sid, [])) subkernel_arg_types=subkernel_arg_types.get(sid, []),
if object_map.has_rpc_or_subkernel(): subkernels=subkernels)
raise ValueError("Subkernel must not use RPC or subkernels in other destinations") if object_map.has_rpc():
return destination, kernel_library raise ValueError("Subkernel must not use RPC")
return destination, kernel_library, object_map
def compile_and_upload_subkernels(self, embedding_map, args, subkernel_arg_types): def compile_and_upload_subkernels(self, embedding_map, args, subkernel_arg_types):
for sid, subkernel_fn in embedding_map.subkernels().items(): subkernels = embedding_map.subkernels()
destination, kernel_library = \ subkernels_compiled = []
self.compile_subkernel(sid, subkernel_fn, embedding_map, while True:
args, subkernel_arg_types) new_subkernels = {}
self.comm.upload_subkernel(kernel_library, sid, destination) for sid, subkernel_fn in subkernels.items():
if sid in subkernels_compiled:
continue
destination, kernel_library, sub_embedding_map = \
self.compile_subkernel(sid, subkernel_fn, embedding_map,
args, subkernel_arg_types, subkernels)
self.comm.upload_subkernel(kernel_library, sid, destination)
new_subkernels.update(sub_embedding_map.subkernels())
subkernels_compiled.append(sid)
if new_subkernels == subkernels:
break
subkernels.update(new_subkernels)
def precompile(self, function, *args, **kwargs): def precompile(self, function, *args, **kwargs):
"""Precompile a kernel and return a callable that executes it on the core device """Precompile a kernel and return a callable that executes it on the core device

View File

@ -460,8 +460,8 @@ extern fn dma_playback(_timestamp: i64, _ptr: i32, _uses_ddma: bool) {
} }
#[unwind(allowed)] #[unwind(allowed)]
extern fn subkernel_load_run(id: u32, run: bool) { extern fn subkernel_load_run(id: u32, destination: u8, run: bool) {
send(&SubkernelLoadRunRequest { id: id, run: run }); send(&SubkernelLoadRunRequest { id: id, destination: destination, run: run });
recv!(&SubkernelLoadRunReply { succeeded } => { recv!(&SubkernelLoadRunReply { succeeded } => {
if !succeeded { if !succeeded {
raise!("SubkernelError", raise!("SubkernelError",
@ -489,9 +489,11 @@ extern fn subkernel_await_finish(id: u32, timeout: u64) {
} }
#[unwind(aborts)] #[unwind(aborts)]
extern fn subkernel_send_message(id: u32, count: u8, tag: &CSlice<u8>, data: *const *const ()) { extern fn subkernel_send_message(id: u32, is_return: bool, destination: u8,
count: u8, tag: &CSlice<u8>, data: *const *const ()) {
send(&SubkernelMsgSend { send(&SubkernelMsgSend {
id: id, id: id,
destination: if is_return { None } else { Some(destination) },
count: count, count: count,
tag: tag.as_ref(), tag: tag.as_ref(),
data: data data: data

View File

@ -116,7 +116,7 @@ pub enum Packet {
DmaRemoveTraceReply { destination: u8, succeeded: bool }, DmaRemoveTraceReply { destination: u8, succeeded: bool },
DmaPlaybackRequest { source: u8, destination: u8, id: u32, timestamp: u64 }, DmaPlaybackRequest { source: u8, destination: u8, id: u32, timestamp: u64 },
DmaPlaybackReply { destination: u8, succeeded: bool }, DmaPlaybackReply { destination: u8, succeeded: bool },
DmaPlaybackStatus { destination: u8, id: u32, error: u8, channel: u32, timestamp: u64 }, DmaPlaybackStatus { source: u8, destination: u8, id: u32, error: u8, channel: u32, timestamp: u64 },
SubkernelAddDataRequest { destination: u8, id: u32, status: PayloadStatus, length: u16, data: [u8; MASTER_PAYLOAD_MAX_SIZE] }, SubkernelAddDataRequest { destination: u8, id: u32, status: PayloadStatus, length: u16, data: [u8; MASTER_PAYLOAD_MAX_SIZE] },
SubkernelAddDataReply { succeeded: bool }, SubkernelAddDataReply { succeeded: bool },
@ -322,6 +322,7 @@ impl Packet {
succeeded: reader.read_bool()? succeeded: reader.read_bool()?
}, },
0xb6 => Packet::DmaPlaybackStatus { 0xb6 => Packet::DmaPlaybackStatus {
source: reader.read_u8()?,
destination: reader.read_u8()?, destination: reader.read_u8()?,
id: reader.read_u32()?, id: reader.read_u32()?,
error: reader.read_u8()?, error: reader.read_u8()?,
@ -617,8 +618,9 @@ impl Packet {
writer.write_u8(destination)?; writer.write_u8(destination)?;
writer.write_bool(succeeded)?; writer.write_bool(succeeded)?;
}, },
Packet::DmaPlaybackStatus { destination, id, error, channel, timestamp } => { Packet::DmaPlaybackStatus { source, destination, id, error, channel, timestamp } => {
writer.write_u8(0xb6)?; writer.write_u8(0xb6)?;
writer.write_u8(source)?;
writer.write_u8(destination)?; writer.write_u8(destination)?;
writer.write_u32(id)?; writer.write_u32(id)?;
writer.write_u8(error)?; writer.write_u8(error)?;

View File

@ -103,11 +103,11 @@ pub enum Message<'a> {
SpiReadReply { succeeded: bool, data: u32 }, SpiReadReply { succeeded: bool, data: u32 },
SpiBasicReply { succeeded: bool }, SpiBasicReply { succeeded: bool },
SubkernelLoadRunRequest { id: u32, run: bool }, SubkernelLoadRunRequest { id: u32, destination: u8, run: bool },
SubkernelLoadRunReply { succeeded: bool }, SubkernelLoadRunReply { succeeded: bool },
SubkernelAwaitFinishRequest { id: u32, timeout: u64 }, SubkernelAwaitFinishRequest { id: u32, timeout: u64 },
SubkernelAwaitFinishReply { status: SubkernelStatus }, SubkernelAwaitFinishReply { status: SubkernelStatus },
SubkernelMsgSend { id: u32, count: u8, tag: &'a [u8], data: *const *const () }, SubkernelMsgSend { id: u32, destination: Option<u8>, count: u8, tag: &'a [u8], data: *const *const () },
SubkernelMsgRecvRequest { id: u32, timeout: u64, tags: &'a [u8] }, SubkernelMsgRecvRequest { id: u32, timeout: u64, tags: &'a [u8] },
SubkernelMsgRecvReply { status: SubkernelStatus, count: u8 }, SubkernelMsgRecvReply { status: SubkernelStatus, count: u8 },

View File

@ -65,7 +65,7 @@ pub mod drtio {
let up_destinations = up_destinations.clone(); let up_destinations = up_destinations.clone();
let ddma_mutex = ddma_mutex.clone(); let ddma_mutex = ddma_mutex.clone();
let subkernel_mutex = subkernel_mutex.clone(); let subkernel_mutex = subkernel_mutex.clone();
io.spawn(8192, move |io| { io.spawn(10240, move |io| {
let routing_table = routing_table.borrow(); let routing_table = routing_table.borrow();
link_thread(io, &aux_mutex, &routing_table, &up_destinations, &ddma_mutex, &subkernel_mutex); link_thread(io, &aux_mutex, &routing_table, &up_destinations, &ddma_mutex, &subkernel_mutex);
}); });
@ -96,17 +96,57 @@ pub mod drtio {
} }
} }
fn process_async_packets(io: &Io, ddma_mutex: &Mutex, subkernel_mutex: &Mutex, fn process_async_packets(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, subkernel_mutex: &Mutex,
routing_table: &drtio_routing::RoutingTable, linkno: u8, packet: drtioaux::Packet routing_table: &drtio_routing::RoutingTable, linkno: u8)
) -> Option<drtioaux::Packet> { {
// returns None if a packet has been consumed or re-routed if link_has_async_ready(linkno) {
macro_rules! route_packet { loop {
($dest:ident) => {{ let reply = aux_transact(io, aux_mutex, linkno, &drtioaux::Packet::RoutingRetrievePackets);
let dest_link = routing_table.0[$dest as usize][0] - 1; if let Ok(packet) = reply {
if dest_link == linkno { match packet {
warn!("[LINK#{}] Re-routed packet would return to the same link, dropping: {:?}", linkno, packet); // packets to be consumed locally
} else if $dest == 0 { drtioaux::Packet::DmaPlaybackStatus { id, source, destination: 0, error, channel, timestamp } => {
warn!("[LINK#{}] Received invalid routable packet: {:?}", linkno, packet) remote_dma::playback_done(io, ddma_mutex, id, source, error, channel, timestamp);
},
drtioaux::Packet::SubkernelFinished { id, destination: 0, with_exception, exception_src } => {
subkernel::subkernel_finished(io, subkernel_mutex, id, with_exception, exception_src);
},
drtioaux::Packet::SubkernelMessage { id, source: from, destination: 0, status, length, data } => {
subkernel::message_handle_incoming(io, subkernel_mutex, id, status, length as usize, &data);
// acknowledge receiving part of the message
drtioaux::send(linkno,
&drtioaux::Packet::SubkernelMessageAck { destination: from }
).unwrap();
},
// routable packets
drtioaux::Packet::DmaAddTraceRequest { destination, .. } |
drtioaux::Packet::DmaAddTraceReply { destination, .. } |
drtioaux::Packet::DmaRemoveTraceRequest { destination, .. } |
drtioaux::Packet::DmaRemoveTraceReply { destination, .. } |
drtioaux::Packet::DmaPlaybackRequest { destination, .. } |
drtioaux::Packet::DmaPlaybackReply { destination, .. } |
drtioaux::Packet::SubkernelLoadRunRequest { destination, .. } |
drtioaux::Packet::SubkernelLoadRunReply { destination, .. } |
drtioaux::Packet::SubkernelMessage { destination, .. } |
drtioaux::Packet::SubkernelMessageAck { destination, .. } |
drtioaux::Packet::DmaPlaybackStatus { destination, .. } |
drtioaux::Packet::SubkernelFinished { destination, .. } => {
let dest_link = routing_table.0[destination as usize][0] - 1;
if dest_link == linkno {
warn!("[LINK#{}] Re-routed packet would return to the same link, dropping: {:?}", linkno, packet);
} else if destination == 0 {
warn!("[LINK#{}] Received invalid routable packet: {:?}", linkno, packet)
} else {
drtioaux::send(dest_link, &packet).unwrap();
}
}
drtioaux::Packet::RoutingNoPackets => break,
other => warn!("[LINK#{}] Received an unroutable packet: {:?}", linkno, other)
}
} else {
warn!("[LINK#{}] Error handling async packets ({})", linkno, reply.unwrap_err());
} }
else { else {
drtioaux::send(dest_link, &packet).unwrap(); drtioaux::send(dest_link, &packet).unwrap();

View File

@ -631,6 +631,8 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
unsafe { kernel::stop() } unsafe { kernel::stop() }
session.kernel_state = KernelState::Absent; session.kernel_state = KernelState::Absent;
unsafe { session.congress.cache.unborrow() } unsafe { session.congress.cache.unborrow() }
#[cfg(has_drtio)]
subkernel::clear_subkernels(io, _subkernel_mutex)?;
match stream { match stream {
None => return Ok(true), None => return Ok(true),
@ -668,7 +670,7 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
} }
} }
#[cfg(has_drtio)] #[cfg(has_drtio)]
&kern::SubkernelLoadRunRequest { id, run } => { &kern::SubkernelLoadRunRequest { id, destination: _, run } => {
let succeeded = match subkernel::load( let succeeded = match subkernel::load(
io, aux_mutex, _subkernel_mutex, routing_table, id, run) { io, aux_mutex, _subkernel_mutex, routing_table, id, run) {
Ok(()) => true, Ok(()) => true,
@ -699,7 +701,7 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
kern_send(io, &kern::SubkernelAwaitFinishReply { status: status }) kern_send(io, &kern::SubkernelAwaitFinishReply { status: status })
} }
#[cfg(has_drtio)] #[cfg(has_drtio)]
&kern::SubkernelMsgSend { id, count, tag, data } => { &kern::SubkernelMsgSend { id, destination: _, count, tag, data } => {
subkernel::message_send(io, aux_mutex, _subkernel_mutex, routing_table, id, count, tag, data)?; subkernel::message_send(io, aux_mutex, _subkernel_mutex, routing_table, id, count, tag, data)?;
kern_acknowledge() kern_acknowledge()
} }

View File

@ -2,10 +2,10 @@ use core::{mem, option::NoneError, cmp::min};
use alloc::{string::String, format, vec::Vec, collections::{btree_map::BTreeMap, vec_deque::VecDeque}}; use alloc::{string::String, format, vec::Vec, collections::{btree_map::BTreeMap, vec_deque::VecDeque}};
use cslice::AsCSlice; use cslice::AsCSlice;
use board_artiq::{mailbox, spi}; use board_artiq::{drtioaux, drtio_routing::RoutingTable, mailbox, spi};
use board_misoc::{csr, clock, i2c}; use board_misoc::{csr, clock, i2c};
use proto_artiq::{ use proto_artiq::{
drtioaux_proto::PayloadStatus, drtioaux_proto::PayloadStatus,
kernel_proto as kern, kernel_proto as kern,
session_proto::Reply::KernelException as HostKernelException, session_proto::Reply::KernelException as HostKernelException,
rpc_proto as rpc}; rpc_proto as rpc};
@ -15,6 +15,7 @@ use kernel::eh_artiq::StackPointerBacktrace;
use ::{cricon_select, RtioMaster}; use ::{cricon_select, RtioMaster};
use cache::Cache; use cache::Cache;
use routing::Router;
use SAT_PAYLOAD_MAX_SIZE; use SAT_PAYLOAD_MAX_SIZE;
use MASTER_PAYLOAD_MAX_SIZE; use MASTER_PAYLOAD_MAX_SIZE;
@ -62,7 +63,9 @@ enum KernelState {
Loaded, Loaded,
Running, Running,
MsgAwait { max_time: u64, tags: Vec<u8> }, MsgAwait { max_time: u64, tags: Vec<u8> },
MsgSending MsgSending,
SubkernelAwaitLoad,
SubkernelAwaitFinish { max_time: u64, id: u32 }
} }
#[derive(Debug)] #[derive(Debug)]
@ -74,6 +77,7 @@ pub enum Error {
NoMessage, NoMessage,
AwaitingMessage, AwaitingMessage,
SubkernelIoError, SubkernelIoError,
DrtioError,
KernelException(Sliceable) KernelException(Sliceable)
} }
@ -89,6 +93,12 @@ impl From<io::Error<!>> for Error {
} }
} }
impl From<drtioaux::Error<!>> for Error {
fn from(_value: drtioaux::Error<!>) -> Error {
Error::DrtioError
}
}
macro_rules! unexpected { macro_rules! unexpected {
($($arg:tt)*) => (return Err(Error::Unexpected(format!($($arg)*)))); ($($arg:tt)*) => (return Err(Error::Unexpected(format!($($arg)*))));
} }
@ -97,7 +107,8 @@ macro_rules! unexpected {
#[derive(Debug)] #[derive(Debug)]
pub struct Sliceable { pub struct Sliceable {
it: usize, it: usize,
data: Vec<u8> data: Vec<u8>,
destination: u8
} }
/* represents interkernel messages */ /* represents interkernel messages */
@ -109,7 +120,6 @@ struct Message {
#[derive(PartialEq)] #[derive(PartialEq)]
enum OutMessageState { enum OutMessageState {
NoMessage, NoMessage,
MessageReady,
MessageBeingSent, MessageBeingSent,
MessageSent, MessageSent,
MessageAcknowledged MessageAcknowledged
@ -129,7 +139,8 @@ struct Session {
log_buffer: String, log_buffer: String,
last_exception: Option<Sliceable>, last_exception: Option<Sliceable>,
source: u8, // which destination requested running the kernel source: u8, // which destination requested running the kernel
messages: MessageManager messages: MessageManager,
subkernels_finished: VecDeque<(u32, bool, u8)> // tuple of id, with_exception, exception_source
} }
#[derive(Debug)] #[derive(Debug)]
@ -154,6 +165,7 @@ pub struct SubkernelFinished {
} }
pub struct SliceMeta { pub struct SliceMeta {
pub destination: u8,
pub len: u16, pub len: u16,
pub status: PayloadStatus pub status: PayloadStatus
} }
@ -169,6 +181,7 @@ macro_rules! get_slice_fn {
self.it += len; self.it += len;
SliceMeta { SliceMeta {
destination: self.destination,
len: len as u16, len: len as u16,
status: status status: status
} }
@ -177,10 +190,11 @@ macro_rules! get_slice_fn {
} }
impl Sliceable { impl Sliceable {
pub fn new(data: Vec<u8>) -> Sliceable { pub fn new(destination: u8, data: Vec<u8>) -> Sliceable {
Sliceable { Sliceable {
it: 0, it: 0,
data: data data: data,
destination: destination
} }
} }
@ -219,17 +233,6 @@ impl MessageManager {
} }
} }
pub fn is_outgoing_ready(&mut self) -> bool {
// called by main loop, to see if there's anything to send, will send it afterwards
match self.out_state {
OutMessageState::MessageReady => {
self.out_state = OutMessageState::MessageBeingSent;
true
},
_ => false
}
}
pub fn was_message_acknowledged(&mut self) -> bool { pub fn was_message_acknowledged(&mut self) -> bool {
match self.out_state { match self.out_state {
OutMessageState::MessageAcknowledged => { OutMessageState::MessageAcknowledged => {
@ -269,14 +272,27 @@ impl MessageManager {
} }
} }
pub fn accept_outgoing(&mut self, count: u8, tag: &[u8], data: *const *const ()) -> Result<(), Error> { pub fn accept_outgoing(&mut self, id: u32, self_destination: u8, destination: u8,
count: u8, tag: &[u8], data: *const *const (),
routing_table: &RoutingTable, rank: u8, router: &mut Router
) -> Result<(), Error> {
let mut writer = Cursor::new(Vec::new()); let mut writer = Cursor::new(Vec::new());
rpc::send_args(&mut writer, 0, tag, data, false)?; rpc::send_args(&mut writer, 0, tag, data, false)?;
// skip service tag, but write the count // skip service tag, but write the count
let mut data = writer.into_inner().split_off(3); let mut data = writer.into_inner().split_off(3);
data[0] = count; data[0] = count;
self.out_message = Some(Sliceable::new(data)); self.out_message = Some(Sliceable::new(destination, data));
self.out_state = OutMessageState::MessageReady;
let mut data_slice: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
self.out_state = OutMessageState::MessageBeingSent;
let meta = self.get_outgoing_slice(&mut data_slice).unwrap();
let res = router.route(drtioaux::Packet::SubkernelMessage {
source: self_destination, destination: destination, id: id,
status: meta.status, length: meta.len as u16, data: data_slice
}, routing_table, rank, self_destination);
if let Err(e) = res {
warn!("error sending SubkernelMessage: {}", e);
}
Ok(()) Ok(())
} }
@ -292,7 +308,8 @@ impl Session {
log_buffer: String::new(), log_buffer: String::new(),
last_exception: None, last_exception: None,
source: 0, source: 0,
messages: MessageManager::new() messages: MessageManager::new(),
subkernels_finished: VecDeque::new()
} }
} }
@ -300,7 +317,8 @@ impl Session {
match self.kernel_state { match self.kernel_state {
KernelState::Absent | KernelState::Loaded => false, KernelState::Absent | KernelState::Loaded => false,
KernelState::Running | KernelState::MsgAwait { .. } | KernelState::Running | KernelState::MsgAwait { .. } |
KernelState::MsgSending => true KernelState::MsgSending | KernelState::SubkernelAwaitLoad |
KernelState::SubkernelAwaitFinish { .. } => true
} }
} }
@ -408,14 +426,6 @@ impl Manager {
self.session.messages.ack_slice() self.session.messages.ack_slice()
} }
pub fn message_is_ready(&mut self) -> bool {
self.session.messages.is_outgoing_ready()
}
pub fn get_last_finished(&mut self) -> Option<SubkernelFinished> {
self.last_finished.take()
}
pub fn load(&mut self, id: u32) -> Result<(), Error> { pub fn load(&mut self, id: u32) -> Result<(), Error> {
if self.current_id == id && self.session.kernel_state == KernelState::Loaded { if self.current_id == id && self.session.kernel_state == KernelState::Loaded {
return Ok(()) return Ok(())
@ -439,6 +449,7 @@ impl Manager {
} }
kern::LoadReply(Err(error)) => { kern::LoadReply(Err(error)) => {
kernel_cpu::stop(); kernel_cpu::stop();
error!("load error: {:?}", error);
Err(Error::Load(format!("{}", error))) Err(Error::Load(format!("{}", error)))
} }
other => { other => {
@ -452,7 +463,7 @@ impl Manager {
pub fn exception_get_slice(&mut self, data_slice: &mut [u8; SAT_PAYLOAD_MAX_SIZE]) -> SliceMeta { pub fn exception_get_slice(&mut self, data_slice: &mut [u8; SAT_PAYLOAD_MAX_SIZE]) -> SliceMeta {
match self.session.last_exception.as_mut() { match self.session.last_exception.as_mut() {
Some(exception) => exception.get_slice_sat(data_slice), Some(exception) => exception.get_slice_sat(data_slice),
None => SliceMeta { len: 0, status: PayloadStatus::FirstAndLast } None => SliceMeta { destination: 0, len: 0, status: PayloadStatus::FirstAndLast }
} }
} }
@ -477,12 +488,18 @@ impl Manager {
backtrace: &[], backtrace: &[],
async_errors: 0 async_errors: 0
}).write_to(&mut writer) { }).write_to(&mut writer) {
Ok(_) => self.session.last_exception = Some(Sliceable::new(writer.into_inner())), Ok(_) => self.session.last_exception = Some(Sliceable::new(0, writer.into_inner())),
Err(_) => error!("Error writing exception data") Err(_) => error!("Error writing exception data")
} }
} }
pub fn process_kern_requests(&mut self, destination: u8) { pub fn process_kern_requests(&mut self, router: &mut Router, routing_table: &RoutingTable, rank: u8, destination: u8) {
macro_rules! finished {
($with_exception:expr) => {{ Some(SubkernelFinished {
source: self.session.source, id: self.current_id,
with_exception: $with_exception, exception_source: destination
}) }}
}
if !self.is_running() { if !self.is_running() {
return; return;
} }
@ -495,34 +512,37 @@ impl Manager {
self.session.kernel_state = KernelState::Absent; self.session.kernel_state = KernelState::Absent;
unsafe { self.cache.unborrow() } unsafe { self.cache.unborrow() }
self.session.last_exception = Some(exception); self.session.last_exception = Some(exception);
self.last_finished = Some(SubkernelFinished { self.last_finished = finished!(true);
source: self.session.source, id: self.current_id, with_exception: true, exception_source: destination
})
}, },
Err(e) => { Err(e) => {
error!("Error while running processing external messages: {:?}", e); error!("Error while running processing external messages: {:?}", e);
self.stop(); self.stop();
self.runtime_exception(e); self.runtime_exception(e);
self.last_finished = Some(SubkernelFinished { self.last_finished = finished!(true);
source: self.session.source, id: self.current_id, with_exception: true, exception_source: destination
})
} }
} }
match self.process_kern_message(destination) { match self.process_kern_message(router, routing_table, rank, destination) {
Ok(Some(with_exception)) => { Ok(Some(with_exception)) => {
self.last_finished = Some(SubkernelFinished { self.last_finished = finished!(with_exception)
source: self.session.source, id: self.current_id, with_exception: with_exception, exception_source: destination
})
}, },
Ok(None) | Err(Error::NoMessage) => (), Ok(None) | Err(Error::NoMessage) => (),
Err(e) => { Err(e) => {
error!("Error while running kernel: {:?}", e); error!("Error while running kernel: {:?}", e);
self.stop(); self.stop();
self.runtime_exception(e); self.runtime_exception(e);
self.last_finished = Some(SubkernelFinished { self.last_finished = finished!(true);
source: self.session.source, id: self.current_id, with_exception: true, exception_source: destination }
}) }
if let Some(subkernel_finished) = self.last_finished.take() {
info!("subkernel {} finished, with exception: {}", subkernel_finished.id, subkernel_finished.with_exception);
let res = router.route(drtioaux::Packet::SubkernelFinished {
destination: subkernel_finished.source, id: subkernel_finished.id,
with_exception: subkernel_finished.with_exception, exception_src: destination
}, &routing_table, rank, destination);
if let Err(e) = res {
warn!("error sending SubkernelFinished: {}", e);
} }
} }
} }
@ -552,11 +572,64 @@ impl Manager {
Err(Error::AwaitingMessage) Err(Error::AwaitingMessage)
} }
}, },
KernelState::SubkernelAwaitFinish { max_time, id } => {
if clock::get_ms() > *max_time {
kern_send(&kern::SubkernelAwaitFinishReply { status: kern::SubkernelStatus::Timeout })?;
self.session.kernel_state = KernelState::Running;
} else {
let mut i = 0;
for status in self.session.subkernels_finished.iter() {
if status.0 == *id {
break;
}
i += 1;
}
if let Some(finish_status) = self.session.subkernels_finished.remove(i) {
if finish_status.1 {
unsafe { kernel_cpu::stop() }
self.session.kernel_state = KernelState::Absent;
unsafe { self.cache.unborrow() }
self.last_finished = Some(SubkernelFinished {
source: self.session.source, id: self.current_id,
with_exception: true, exception_source: finish_status.2
})
} else {
kern_send(&kern::SubkernelAwaitFinishReply { status: kern::SubkernelStatus::NoError })?;
self.session.kernel_state = KernelState::Running;
}
}
}
Ok(())
}
_ => Ok(()) _ => Ok(())
} }
} }
fn process_kern_message(&mut self, destination: u8) -> Result<Option<bool>, Error> { pub fn subkernel_load_run_reply(&mut self, succeeded: bool, self_destination: u8) {
if self.session.kernel_state == KernelState::SubkernelAwaitLoad {
if let Err(e) = kern_send(&kern::SubkernelLoadRunReply { succeeded: succeeded }) {
self.stop();
self.runtime_exception(e);
self.last_finished = Some(SubkernelFinished {
source: self.session.source, id: self.current_id,
with_exception: true, exception_source: self_destination
})
} else {
self.session.kernel_state = KernelState::Running;
}
} else {
warn!("received unsolicited SubkernelLoadRunReply");
}
}
pub fn remote_subkernel_finished(&mut self, id: u32, with_exception: bool, exception_source: u8) {
self.session.subkernels_finished.push_back((id, with_exception, exception_source));
}
fn process_kern_message(&mut self, router: &mut Router,
routing_table: &RoutingTable,
rank: u8, destination: u8
) -> Result<Option<bool>, Error> {
// returns Ok(with_exception) on finish // returns Ok(with_exception) on finish
// None if the kernel is still running // None if the kernel is still running
kern_recv(|request| { kern_recv(|request| {
@ -626,8 +699,14 @@ impl Manager {
return Ok(Some(true)) return Ok(Some(true))
} }
&kern::SubkernelMsgSend { id: _, count, tag, data } => { &kern::SubkernelMsgSend { id: _, destination: msg_dest, count, tag, data } => {
self.session.messages.accept_outgoing(count, tag, data)?; let dest = match msg_dest {
Some(dest) => dest,
None => self.session.source
};
self.session.messages.accept_outgoing(self.current_id, destination,
dest, count, tag, data,
routing_table, rank, router)?;
// acknowledge after the message is sent // acknowledge after the message is sent
self.session.kernel_state = KernelState::MsgSending; self.session.kernel_state = KernelState::MsgSending;
Ok(()) Ok(())
@ -639,6 +718,20 @@ impl Manager {
Ok(()) Ok(())
}, },
&kern::SubkernelLoadRunRequest { id, destination: sk_destination, run } => {
self.session.kernel_state = KernelState::SubkernelAwaitLoad;
router.route(drtioaux::Packet::SubkernelLoadRunRequest {
source: destination, destination: sk_destination, id: id, run: run
}, routing_table, rank, destination).map_err(|_| Error::DrtioError)?;
kern_acknowledge()
}
&kern::SubkernelAwaitFinishRequest{ id, timeout } => {
let max_time = clock::get_ms() + timeout as u64;
self.session.kernel_state = KernelState::SubkernelAwaitFinish { max_time: max_time, id: id };
kern_acknowledge()
}
request => unexpected!("unexpected request {:?} from kernel CPU", request) request => unexpected!("unexpected request {:?} from kernel CPU", request)
}.and(Ok(None)) }.and(Ok(None))
}) })
@ -712,7 +805,7 @@ fn slice_kernel_exception(exceptions: &[Option<eh_artiq::Exception>],
async_errors: 0 async_errors: 0
}).write_to(&mut writer) { }).write_to(&mut writer) {
// save last exception data to be received by master // save last exception data to be received by master
Ok(_) => Ok(Sliceable::new(writer.into_inner())), Ok(_) => Ok(Sliceable::new(0, writer.into_inner())),
Err(_) => Err(Error::SubkernelIoError) Err(_) => Err(Error::SubkernelIoError)
} }
} }

View File

@ -132,46 +132,46 @@ fn process_aux_packet(dmamgr: &mut DmaManager, analyzer: &mut Analyzer, kernelmg
let hop = 0; let hop = 0;
if hop == 0 { if hop == 0 {
*self_destination = destination;
// async messages // async messages
if *rank == 1 { if *rank == 1 {
if let Some(packet) = router.get_upstream_packet(*rank) { if let Some(packet) = router.get_upstream_packet(*rank) {
// pass any async or routed packets to master // pass any async or routed packets to master
// this does mean that DDMA/SK packets to master will "trickle down" to higher rank // this does mean that DDMA/SK packets to master will "trickle down" to higher rank
drtioaux::send(0, &packet)?; return drtioaux::send(0, &packet)
} }
} else { }
let errors; let errors;
unsafe {
errors = csr::drtiosat::rtio_error_read();
}
if errors & 1 != 0 {
let channel;
unsafe { unsafe {
errors = csr::drtiosat::rtio_error_read(); channel = csr::drtiosat::sequence_error_channel_read();
csr::drtiosat::rtio_error_write(1);
} }
if errors & 1 != 0 { drtioaux::send(0,
let channel; &drtioaux::Packet::DestinationSequenceErrorReply { channel })?;
unsafe { } else if errors & 2 != 0 {
channel = csr::drtiosat::sequence_error_channel_read(); let channel;
csr::drtiosat::rtio_error_write(1); unsafe {
} channel = csr::drtiosat::collision_channel_read();
drtioaux::send(0, csr::drtiosat::rtio_error_write(2);
&drtioaux::Packet::DestinationSequenceErrorReply { channel })?;
} else if errors & 2 != 0 {
let channel;
unsafe {
channel = csr::drtiosat::collision_channel_read();
csr::drtiosat::rtio_error_write(2);
}
drtioaux::send(0,
&drtioaux::Packet::DestinationCollisionReply { channel })?;
} else if errors & 4 != 0 {
let channel;
unsafe {
channel = csr::drtiosat::busy_channel_read();
csr::drtiosat::rtio_error_write(4);
}
drtioaux::send(0,
&drtioaux::Packet::DestinationBusyReply { channel })?;
} }
else { drtioaux::send(0,
drtioaux::send(0, &drtioaux::Packet::DestinationOkReply)?; &drtioaux::Packet::DestinationCollisionReply { channel })?;
} else if errors & 4 != 0 {
let channel;
unsafe {
channel = csr::drtiosat::busy_channel_read();
csr::drtiosat::rtio_error_write(4);
} }
drtioaux::send(0,
&drtioaux::Packet::DestinationBusyReply { channel })?;
}
else {
drtioaux::send(0, &drtioaux::Packet::DestinationOkReply)?;
} }
} }
@ -196,7 +196,6 @@ fn process_aux_packet(dmamgr: &mut DmaManager, analyzer: &mut Analyzer, kernelmg
} }
} }
} }
Ok(()) Ok(())
} }
@ -378,14 +377,14 @@ fn process_aux_packet(dmamgr: &mut DmaManager, analyzer: &mut Analyzer, kernelmg
let succeeded = dmamgr.add(source, id, status, &trace, length as usize).is_ok(); let succeeded = dmamgr.add(source, id, status, &trace, length as usize).is_ok();
router.send(drtioaux::Packet::DmaAddTraceReply { router.send(drtioaux::Packet::DmaAddTraceReply {
destination: source, succeeded: succeeded destination: source, succeeded: succeeded
}, _routing_table, *rank) }, _routing_table, *rank, *self_destination)
} }
drtioaux::Packet::DmaRemoveTraceRequest { source, destination: _destination, id } => { drtioaux::Packet::DmaRemoveTraceRequest { source, destination: _destination, id } => {
forward!(_routing_table, _destination, *rank, _repeaters, &packet); forward!(_routing_table, _destination, *rank, _repeaters, &packet);
let succeeded = dmamgr.erase(source, id).is_ok(); let succeeded = dmamgr.erase(source, id).is_ok();
router.send(drtioaux::Packet::DmaRemoveTraceReply { router.send(drtioaux::Packet::DmaRemoveTraceReply {
destination: source, succeeded: succeeded destination: source, succeeded: succeeded
}, _routing_table, *rank) }, _routing_table, *rank, *self_destination)
} }
drtioaux::Packet::DmaPlaybackRequest { source, destination: _destination, id, timestamp } => { drtioaux::Packet::DmaPlaybackRequest { source, destination: _destination, id, timestamp } => {
forward!(_routing_table, _destination, *rank, _repeaters, &packet); forward!(_routing_table, _destination, *rank, _repeaters, &packet);
@ -393,7 +392,7 @@ fn process_aux_packet(dmamgr: &mut DmaManager, analyzer: &mut Analyzer, kernelmg
let succeeded = !kernelmgr.is_running() && dmamgr.playback(source, id, timestamp).is_ok(); let succeeded = !kernelmgr.is_running() && dmamgr.playback(source, id, timestamp).is_ok();
router.send(drtioaux::Packet::DmaPlaybackReply { router.send(drtioaux::Packet::DmaPlaybackReply {
destination: source, succeeded: succeeded destination: source, succeeded: succeeded
}, _routing_table, *rank) }, _routing_table, *rank, *self_destination)
} }
drtioaux::Packet::SubkernelAddDataRequest { destination, id, status, length, data } => { drtioaux::Packet::SubkernelAddDataRequest { destination, id, status, length, data } => {
@ -418,7 +417,19 @@ fn process_aux_packet(dmamgr: &mut DmaManager, analyzer: &mut Analyzer, kernelmg
router.send(drtioaux::Packet::SubkernelLoadRunReply { router.send(drtioaux::Packet::SubkernelLoadRunReply {
destination: source, succeeded: succeeded destination: source, succeeded: succeeded
}, },
_routing_table, *rank) _routing_table, *rank, *self_destination)
}
drtioaux::Packet::SubkernelLoadRunReply { destination: _destination, succeeded } => {
forward!(_routing_table, _destination, *rank, _repeaters, &packet);
// received if local subkernel started another, remote subkernel
kernelmgr.subkernel_load_run_reply(succeeded, *self_destination);
Ok(())
}
// { destination: u8, id: u32, with_exception: bool, exception_src: u8 },
drtioaux::Packet::SubkernelFinished { destination: _destination, id, with_exception, exception_src } => {
forward!(_routing_table, _destination, *rank, _repeaters, &packet);
kernelmgr.remote_subkernel_finished(id, with_exception, exception_src);
Ok(())
} }
drtioaux::Packet::SubkernelExceptionRequest { destination: _destination } => { drtioaux::Packet::SubkernelExceptionRequest { destination: _destination } => {
forward!(_routing_table, _destination, *rank, _repeaters, &packet); forward!(_routing_table, _destination, *rank, _repeaters, &packet);
@ -435,7 +446,7 @@ fn process_aux_packet(dmamgr: &mut DmaManager, analyzer: &mut Analyzer, kernelmg
kernelmgr.message_handle_incoming(status, length as usize, &data); kernelmgr.message_handle_incoming(status, length as usize, &data);
router.send(drtioaux::Packet::SubkernelMessageAck { router.send(drtioaux::Packet::SubkernelMessageAck {
destination: source destination: source
}, _routing_table, *rank) }, _routing_table, *rank, *self_destination)
} }
drtioaux::Packet::SubkernelMessageAck { destination: _destination } => { drtioaux::Packet::SubkernelMessageAck { destination: _destination } => {
forward!(_routing_table, _destination, *rank, _repeaters, &packet); forward!(_routing_table, _destination, *rank, _repeaters, &packet);
@ -443,10 +454,9 @@ fn process_aux_packet(dmamgr: &mut DmaManager, analyzer: &mut Analyzer, kernelmg
let mut data_slice: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE]; let mut data_slice: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
if let Some(meta) = kernelmgr.message_get_slice(&mut data_slice) { if let Some(meta) = kernelmgr.message_get_slice(&mut data_slice) {
router.send(drtioaux::Packet::SubkernelMessage { router.send(drtioaux::Packet::SubkernelMessage {
source: *self_destination, destination: 0, id: kernelmgr.get_current_id().unwrap(), source: *self_destination, destination: meta.destination, id: kernelmgr.get_current_id().unwrap(),
status: meta.status, length: meta.len as u16, data: data_slice status: meta.status, length: meta.len as u16, data: data_slice
}, }, _routing_table, *rank, *self_destination)?;
_routing_table, *rank)?;
} else { } else {
error!("Error receiving message slice"); error!("Error receiving message slice");
} }
@ -694,7 +704,7 @@ pub extern fn main() -> i32 {
while !drtiosat_link_rx_up() { while !drtiosat_link_rx_up() {
drtiosat_process_errors(); drtiosat_process_errors();
for rep in repeaters.iter_mut() { for rep in repeaters.iter_mut() {
rep.service(&routing_table, rank, &mut router); rep.service(&routing_table, rank, destination, &mut router);
} }
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))] #[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
{ {
@ -731,7 +741,7 @@ pub extern fn main() -> i32 {
&mut kernelmgr, &mut repeaters, &mut routing_table, &mut kernelmgr, &mut repeaters, &mut routing_table,
&mut rank, &mut router, &mut destination); &mut rank, &mut router, &mut destination);
for rep in repeaters.iter_mut() { for rep in repeaters.iter_mut() {
rep.service(&routing_table, rank, &mut router); rep.service(&routing_table, rank, destination, &mut router);
} }
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))] #[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
{ {
@ -754,34 +764,21 @@ pub extern fn main() -> i32 {
} }
if let Some(status) = dma_manager.get_status() { if let Some(status) = dma_manager.get_status() {
info!("playback done, error: {}, channel: {}, timestamp: {}", status.error, status.channel, status.timestamp); info!("playback done, error: {}, channel: {}, timestamp: {}", status.error, status.channel, status.timestamp);
let res = router.route(drtioaux::Packet::DmaPlaybackStatus { router.route(drtioaux::Packet::DmaPlaybackStatus {
destination: status.source, id: status.id, error: status.error, source: destination, destination: status.source, id: status.id,
channel: status.channel, timestamp: status.timestamp error: status.error, channel: status.channel, timestamp: status.timestamp
}, &routing_table, rank); }, &routing_table, rank, destination);
if let Err(e) = res {
warn!("error sending DmaPlaybackStatus: {}", e);
}
} }
kernelmgr.process_kern_requests(destination);
if let Some(subkernel_finished) = kernelmgr.get_last_finished() { kernelmgr.process_kern_requests(&mut router, &routing_table, rank, destination);
info!("subkernel {} finished, with exception: {}", subkernel_finished.id, subkernel_finished.with_exception);
let res = router.route(drtioaux::Packet::SubkernelFinished { if rank > 1 {
destination: subkernel_finished.source, id: subkernel_finished.id, if let Some(packet) = router.get_upstream_packet(rank) {
with_exception: subkernel_finished.with_exception, exception_src: destination // in sat-sat communications, it can be async
}, &routing_table, rank); let res = drtioaux::send(0, &packet);
if let Err(e) = res { if let Err(e) = res {
warn!("error sending SubkernelFinished: {}", e); warn!("error routing packet: {}", e);
} }
}
if kernelmgr.message_is_ready() {
let mut data_slice: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
let meta = kernelmgr.message_get_slice(&mut data_slice).unwrap();
let res = router.route(drtioaux::Packet::SubkernelMessage {
source: destination, destination: 0, id: kernelmgr.get_current_id().unwrap(),
status: meta.status, length: meta.len as u16, data: data_slice
}, &routing_table, rank);
if let Err(e) = res {
warn!("error sending SubkernelMessage: {}", e);
} }
} }
} }

View File

@ -49,7 +49,7 @@ impl Repeater {
self.state == RepeaterState::Up self.state == RepeaterState::Up
} }
pub fn service(&mut self, routing_table: &drtio_routing::RoutingTable, rank: u8, router: &mut Router) { pub fn service(&mut self, routing_table: &drtio_routing::RoutingTable, rank: u8, destination: u8, router: &mut Router) {
self.process_local_errors(); self.process_local_errors();
match self.state { match self.state {
@ -107,7 +107,7 @@ impl Repeater {
} }
} }
RepeaterState::Up => { RepeaterState::Up => {
self.process_unsolicited_aux(routing_table, rank, router); self.process_unsolicited_aux(routing_table, rank, destination, router);
if !rep_link_rx_up(self.repno) { if !rep_link_rx_up(self.repno) {
info!("[REP#{}] link is down", self.repno); info!("[REP#{}] link is down", self.repno);
self.state = RepeaterState::Down; self.state = RepeaterState::Down;
@ -122,7 +122,7 @@ impl Repeater {
} }
} }
fn process_unsolicited_aux(&self, routing_table: &drtio_routing::RoutingTable, rank: u8, router: &mut Router) { fn process_unsolicited_aux(&self, routing_table: &drtio_routing::RoutingTable, rank: u8, self_destination: u8, router: &mut Router) {
match drtioaux::recv(self.auxno) { match drtioaux::recv(self.auxno) {
Ok(Some(packet)) => { Ok(Some(packet)) => {
let destination = get_routable_packet_destination(&packet); let destination = get_routable_packet_destination(&packet);
@ -130,7 +130,7 @@ impl Repeater {
warn!("[REP#{}] unsolicited aux packet: {:?}", self.repno, packet); warn!("[REP#{}] unsolicited aux packet: {:?}", self.repno, packet);
} else { } else {
// routable packet // routable packet
let res = router.route(packet, routing_table, rank); let res = router.route(packet, routing_table, rank, self_destination);
match res { match res {
Ok(()) => drtioaux::send(self.auxno, &drtioaux::Packet::RoutingAck).unwrap(), Ok(()) => drtioaux::send(self.auxno, &drtioaux::Packet::RoutingAck).unwrap(),
Err(e) => warn!("[REP#{}] Error routing packet: {:?}", self.repno, e), Err(e) => warn!("[REP#{}] Error routing packet: {:?}", self.repno, e),
@ -212,9 +212,8 @@ impl Repeater {
(csr::DRTIOREP[repno].set_time_write)(1); (csr::DRTIOREP[repno].set_time_write)(1);
while (csr::DRTIOREP[repno].set_time_read)() == 1 {} while (csr::DRTIOREP[repno].set_time_read)() == 1 {}
} }
// TSCAck is sent spontaneously by the satellite,
// TSCAck is the only aux packet that is sent spontaneously // in response to a TSC set on the RT link.
// by the satellite, in response to a TSC set on the RT link.
let reply = self.recv_aux_timeout(10000)?; let reply = self.recv_aux_timeout(10000)?;
if reply == drtioaux::Packet::TSCAck { if reply == drtioaux::Packet::TSCAck {
return Ok(()); return Ok(());
@ -288,7 +287,7 @@ pub struct Repeater {
impl Repeater { impl Repeater {
pub fn new(_repno: u8) -> Repeater { Repeater::default() } pub fn new(_repno: u8) -> Repeater { Repeater::default() }
pub fn service(&self, _routing_table: &drtio_routing::RoutingTable, _rank: u8) { } pub fn service(&self, _routing_table: &drtio_routing::RoutingTable, _rank: u8, _destination: u8, _router: &mut Router) { }
pub fn sync_tsc(&self) -> Result<(), drtioaux::Error<!>> { Ok(()) } pub fn sync_tsc(&self) -> Result<(), drtioaux::Error<!>> { Ok(()) }

View File

@ -1,6 +1,5 @@
use alloc::collections::vec_deque::VecDeque; use alloc::collections::vec_deque::VecDeque;
use board_artiq::{drtioaux, drtio_routing}; use board_artiq::{drtioaux, drtio_routing};
use board_misoc::csr;
// Packets from downstream (further satellites) are received and routed appropriately. // Packets from downstream (further satellites) are received and routed appropriately.
// they're passed immediately if it's possible (within the subtree), or sent upstream. // they're passed immediately if it's possible (within the subtree), or sent upstream.
@ -51,7 +50,8 @@ impl Router {
// called by local sources (DDMA, kernel) and by repeaters on receiving unsolicited data // called by local sources (DDMA, kernel) and by repeaters on receiving unsolicited data
// messages are always buffered for upstream, or passed downstream directly // messages are always buffered for upstream, or passed downstream directly
pub fn route(&mut self, packet: drtioaux::Packet, pub fn route(&mut self, packet: drtioaux::Packet,
_routing_table: &drtio_routing::RoutingTable, _rank: u8 _routing_table: &drtio_routing::RoutingTable, _rank: u8,
_self_destination: u8
) -> Result<(), drtioaux::Error<!>> { ) -> Result<(), drtioaux::Error<!>> {
#[cfg(has_drtio_routing)] #[cfg(has_drtio_routing)]
{ {
@ -59,14 +59,12 @@ impl Router {
if let Some(destination) = destination { if let Some(destination) = destination {
let hop = _routing_table.0[destination as usize][_rank as usize]; let hop = _routing_table.0[destination as usize][_rank as usize];
let auxno = if destination == 0 { 0 } else { hop }; let auxno = if destination == 0 { 0 } else { hop };
if hop != 0 { if destination == _self_destination {
if hop as usize <= csr::DRTIOREP.len() {
drtioaux::send(auxno, &packet)?;
} else {
self.out_messages.push_back(packet);
}
} else {
self.local_messages.push_back(packet); self.local_messages.push_back(packet);
} else if _rank > 1 {
drtioaux::send(auxno, &packet)?;
} else {
self.out_messages.push_back(packet);
} }
} else { } else {
return Err(drtioaux::Error::RoutingError); return Err(drtioaux::Error::RoutingError);
@ -81,7 +79,7 @@ impl Router {
// Sends a packet to a required destination, routing if it's necessary // Sends a packet to a required destination, routing if it's necessary
pub fn send(&mut self, packet: drtioaux::Packet, pub fn send(&mut self, packet: drtioaux::Packet,
_routing_table: &drtio_routing::RoutingTable, _rank: u8) -> Result<(), drtioaux::Error<!>> { _routing_table: &drtio_routing::RoutingTable, _rank: u8, _destination: u8) -> Result<(), drtioaux::Error<!>> {
#[cfg(has_drtio_routing)] #[cfg(has_drtio_routing)]
{ {
let destination = get_routable_packet_destination(&packet); let destination = get_routable_packet_destination(&packet);
@ -89,7 +87,7 @@ impl Router {
// send upstream directly (response to master) // send upstream directly (response to master)
drtioaux::send(0, &packet) drtioaux::send(0, &packet)
} else { } else {
self.route(packet, _routing_table, _rank) self.route(packet, _routing_table, _rank, _destination)
} }
} }
#[cfg(not(has_drtio_routing))] #[cfg(not(has_drtio_routing))]

View File

@ -67,12 +67,21 @@ def main():
core.compile(exp.run, [exp_inst], {}, core.compile(exp.run, [exp_inst], {},
attribute_writeback=False, print_as_rpc=False) attribute_writeback=False, print_as_rpc=False)
subkernels = {} subkernels = object_map.subkernels()
for sid, subkernel_fn in object_map.subkernels().items(): compiled_subkernels = {}
destination, subkernel_library = core.compile_subkernel( while True:
sid, subkernel_fn, object_map, new_subkernels = {}
[exp_inst], subkernel_arg_types) for sid, subkernel_fn in subkernels.items():
subkernels[sid] = (destination, subkernel_library) if sid in compiled_subkernels.keys():
continue
destination, subkernel_library, embedding_map = core.compile_subkernel(
sid, subkernel_fn, object_map,
[exp_inst], subkernel_arg_types, subkernels)
compiled_subkernels[sid] = (destination, subkernel_library)
new_subkernels.update(embedding_map.subkernels())
if new_subkernels == subkernels:
break
subkernels.update(new_subkernels)
except CompileError as error: except CompileError as error:
return return
finally: finally:
@ -107,7 +116,7 @@ def main():
tar.addfile(main_kernel_info, fileobj=main_kernel_fileobj) tar.addfile(main_kernel_info, fileobj=main_kernel_fileobj)
# subkernels as "<sid> <destination>.elf" # subkernels as "<sid> <destination>.elf"
for sid, (destination, subkernel_library) in subkernels.items(): for sid, (destination, subkernel_library) in compiled_subkernels.items():
subkernel_fileobj = io.BytesIO(subkernel_library) subkernel_fileobj = io.BytesIO(subkernel_library)
subkernel_info = tarfile.TarInfo(name="{} {}.elf".format(sid, destination)) subkernel_info = tarfile.TarInfo(name="{} {}.elf".format(sid, destination))
subkernel_info.size = len(subkernel_library) subkernel_info.size = len(subkernel_library)

View File

@ -6,13 +6,13 @@ from artiq.language.types import *
@kernel @kernel
def entrypoint(): def entrypoint():
# CHECK: call void @subkernel_load_run\(i32 1, i1 true\), !dbg !. # CHECK: call void @subkernel_load_run\(i32 1, i8 1, i1 true\), !dbg !.
# CHECK-NOT: call void @subkernel_send_message\(.*\), !dbg !. # CHECK-NOT: call void @subkernel_send_message\(.*\), !dbg !.
no_arg() no_arg()
# CHECK-L: declare void @subkernel_load_run(i32, i1) local_unnamed_addr # CHECK-L: declare void @subkernel_load_run(i32, i8, i1) local_unnamed_addr
# CHECK-NOT-L: declare void @subkernel_send_message(i32, { i8*, i32 }*, i8**) local_unnamed_addr # CHECK-NOT-L: declare void @subkernel_send_message(i32, i1, i8, { i8*, i32 }*, i8**) local_unnamed_addr
@subkernel(destination=1) @subkernel(destination=1)
def no_arg() -> TStr: def no_arg() -> TStr:
pass pass

View File

@ -6,15 +6,15 @@ from artiq.language.types import *
@kernel @kernel
def entrypoint(): def entrypoint():
# CHECK: call void @subkernel_load_run\(i32 1, i1 true\), !dbg !. # CHECK: call void @subkernel_load_run\(i32 1, i8 1, i1 true\), !dbg !.
# CHECK-NOT: call void @subkernel_send_message\(.*\), !dbg !. # CHECK-NOT: call void @subkernel_send_message\(.*\), !dbg !.
returning() returning()
# CHECK: call i8 @subkernel_await_message\(i32 1, i64 10000, { i8\*, i32 }\* nonnull .*, i8 1, i8 1\), !dbg !. # CHECK: call i8 @subkernel_await_message\(i32 1, i64 10000, { i8\*, i32 }\* nonnull .*, i8 1, i8 1\), !dbg !.
# CHECK: call void @subkernel_await_finish\(i32 1, i64 10000\), !dbg !. # CHECK: call void @subkernel_await_finish\(i32 1, i64 10000\), !dbg !.
subkernel_await(returning) subkernel_await(returning)
# CHECK-L: declare void @subkernel_load_run(i32, i1) local_unnamed_addr # CHECK-L: declare void @subkernel_load_run(i32, i8, i1) local_unnamed_addr
# CHECK-NOT-L: declare void @subkernel_send_message(i32, i8, { i8*, i32 }*, i8**) local_unnamed_addr # CHECK-NOT-L: declare void @subkernel_send_message(i32, i1, i8, i8, { i8*, i32 }*, i8**) local_unnamed_addr
# CHECK-L: declare i8 @subkernel_await_message(i32, i64, { i8*, i32 }*, i8, i8) local_unnamed_addr # CHECK-L: declare i8 @subkernel_await_message(i32, i64, { i8*, i32 }*, i8, i8) local_unnamed_addr
# CHECK-L: declare void @subkernel_await_finish(i32, i64) local_unnamed_addr # CHECK-L: declare void @subkernel_await_finish(i32, i64) local_unnamed_addr
@subkernel(destination=1) @subkernel(destination=1)

View File

@ -6,15 +6,15 @@ from artiq.language.types import *
@kernel @kernel
def entrypoint(): def entrypoint():
# CHECK: call void @subkernel_load_run\(i32 1, i1 true\), !dbg !. # CHECK: call void @subkernel_load_run\(i32 1, i8 1, i1 true\), !dbg !.
# CHECK-NOT: call void @subkernel_send_message\(.*\), !dbg !. # CHECK-NOT: call void @subkernel_send_message\(.*\), !dbg !.
returning_none() returning_none()
# CHECK: call void @subkernel_await_finish\(i32 1, i64 10000\), !dbg !. # CHECK: call void @subkernel_await_finish\(i32 1, i64 10000\), !dbg !.
# CHECK-NOT: call i8 @subkernel_await_message\(i32 1, i64 10000\, .*\), !dbg !. # CHECK-NOT: call i8 @subkernel_await_message\(i32 1, i64 10000\, .*\), !dbg !.
subkernel_await(returning_none) subkernel_await(returning_none)
# CHECK-L: declare void @subkernel_load_run(i32, i1) local_unnamed_addr # CHECK-L: declare void @subkernel_load_run(i32, i8, i1) local_unnamed_addr
# CHECK-NOT-L: declare void @subkernel_send_message(i32, { i8*, i32 }*, i8**) local_unnamed_addr # CHECK-NOT-L: declare void @subkernel_send_message(i32, i1, i8, { i8*, i32 }*, i8**) local_unnamed_addr
# CHECK-L: declare void @subkernel_await_finish(i32, i64) local_unnamed_addr # CHECK-L: declare void @subkernel_await_finish(i32, i64) local_unnamed_addr
# CHECK-NOT-L: declare i8 @subkernel_await_message(i32, i64, { i8*, i32 }*, i8, i8) local_unnamed_addr # CHECK-NOT-L: declare i8 @subkernel_await_message(i32, i64, { i8*, i32 }*, i8, i8) local_unnamed_addr
@subkernel(destination=1) @subkernel(destination=1)

View File

@ -11,7 +11,7 @@ class A:
@kernel @kernel
def kernel_entrypoint(self): def kernel_entrypoint(self):
# CHECK: call void @subkernel_load_run\(i32 1, i1 true\), !dbg !. # CHECK: call void @subkernel_load_run\(i32 1, i8 1, i1 true\), !dbg !.
# CHECK-NOT: call void @subkernel_send_message\(.*\), !dbg !. # CHECK-NOT: call void @subkernel_send_message\(.*\), !dbg !.
self.sk() self.sk()
@ -21,5 +21,5 @@ a = A()
def entrypoint(): def entrypoint():
a.kernel_entrypoint() a.kernel_entrypoint()
# CHECK-L: declare void @subkernel_load_run(i32, i1) local_unnamed_addr # CHECK-L: declare void @subkernel_load_run(i32, i8, i1) local_unnamed_addr
# CHECK-NOT-L: declare void @subkernel_send_message(i32, i8, { i8*, i32 }*, i8**) local_unnamed_addr # CHECK-NOT-L: declare void @subkernel_send_message(i32, i1, i8, i8, { i8*, i32 }*, i8**) local_unnamed_addr

View File

@ -11,8 +11,8 @@ class A:
@kernel @kernel
def kernel_entrypoint(self): def kernel_entrypoint(self):
# CHECK: call void @subkernel_load_run\(i32 1, i1 true\), !dbg !. # CHECK: call void @subkernel_load_run\(i32 1, i8 1, i1 true\), !dbg !.
# CHECK: call void @subkernel_send_message\(i32 1, i8 1, .*\), !dbg !. # CHECK: call void @subkernel_send_message\(i32 1, i1 false, i8 1, i8 1, .*\), !dbg !.
self.sk(1) self.sk(1)
a = A() a = A()
@ -21,5 +21,5 @@ a = A()
def entrypoint(): def entrypoint():
a.kernel_entrypoint() a.kernel_entrypoint()
# CHECK-L: declare void @subkernel_load_run(i32, i1) local_unnamed_addr # CHECK-L: declare void @subkernel_load_run(i32, i8, i1) local_unnamed_addr
# CHECK-L: declare void @subkernel_send_message(i32, i8, { i8*, i32 }*, i8**) local_unnamed_addr # CHECK-L: declare void @subkernel_send_message(i32, i1, i8, i8, { i8*, i32 }*, i8**) local_unnamed_addr

View File

@ -6,13 +6,13 @@ from artiq.language.types import *
@kernel @kernel
def entrypoint(): def entrypoint():
# CHECK: call void @subkernel_load_run\(i32 1, i1 true\), !dbg !. # CHECK: call void @subkernel_load_run\(i32 1, i8 1, i1 true\), !dbg !.
# CHECK: call void @subkernel_send_message\(i32 ., i8 1, .*\), !dbg !. # CHECK: call void @subkernel_send_message\(i32 ., i1 false, i8 1, i8 1, .*\), !dbg !.
accept_arg(1) accept_arg(1)
# CHECK-L: declare void @subkernel_load_run(i32, i1) local_unnamed_addr # CHECK-L: declare void @subkernel_load_run(i32, i8, i1) local_unnamed_addr
# CHECK-L: declare void @subkernel_send_message(i32, i8, { i8*, i32 }*, i8**) local_unnamed_addr # CHECK-L: declare void @subkernel_send_message(i32, i1, i8, i8, { i8*, i32 }*, i8**) local_unnamed_addr
@subkernel(destination=1) @subkernel(destination=1)
def accept_arg(arg: TInt32) -> TNone: def accept_arg(arg: TInt32) -> TNone:
pass pass

View File

@ -6,16 +6,16 @@ from artiq.language.types import *
@kernel @kernel
def entrypoint(): def entrypoint():
# CHECK: call void @subkernel_load_run\(i32 1, i1 true\), !dbg !. # CHECK: call void @subkernel_load_run\(i32 1, i8 1, i1 true\), !dbg !.
# CHECK: call void @subkernel_send_message\(i32 ., i8 1, .*\), !dbg !. # CHECK: call void @subkernel_send_message\(i32 ., i1 false, i8 1, i8 1, .*\), !dbg !.
accept_arg(1) accept_arg(1)
# CHECK: call void @subkernel_load_run\(i32 1, i1 true\), !dbg !. # CHECK: call void @subkernel_load_run\(i32 1, i8 1, i1 true\), !dbg !.
# CHECK: call void @subkernel_send_message\(i32 ., i8 2, .*\), !dbg !. # CHECK: call void @subkernel_send_message\(i32 ., i1 false, i8 1, i8 2, .*\), !dbg !.
accept_arg(1, 2) accept_arg(1, 2)
# CHECK-L: declare void @subkernel_load_run(i32, i1) local_unnamed_addr # CHECK-L: declare void @subkernel_load_run(i32, i8, i1) local_unnamed_addr
# CHECK-L: declare void @subkernel_send_message(i32, i8, { i8*, i32 }*, i8**) local_unnamed_addr # CHECK-L: declare void @subkernel_send_message(i32, i1, i8, i8, { i8*, i32 }*, i8**) local_unnamed_addr
@subkernel(destination=1) @subkernel(destination=1)
def accept_arg(arg_a, arg_b=5) -> TNone: def accept_arg(arg_a, arg_b=5) -> TNone:
pass pass