use crate::sidecar::backend::{Access, AccessKind, BackendError, Reply, Stop};
use crate::sidecar::layout::{ControlBlock, InterruptLevels, Version};
use crate::sidecar::message::{CoreMessage, SidecarMessage};
use crate::sidecar::turn::{Turn, WaitError};
use crate::time::VirtualNanos;
#[derive(Debug)]
pub struct Link<'a, Check> {
block: &'a ControlBlock,
check: Check,
}
impl<'a, Check: FnMut() -> Result<(), WaitError>> Link<'a, Check> {
pub fn new(block: &'a ControlBlock, check: Check) -> Self {
Self { block, check }
}
pub fn handshake(&mut self, expected: Version) -> Result<(), BackendError> {
match self.receive()? {
SidecarMessage::Hello { version } if version == expected => Ok(()),
SidecarMessage::Hello { version } => {
self.send(CoreMessage::Shutdown);
Err(BackendError::Version {
sidecar: version,
expected,
})
}
message => Err(BackendError::Unexpected { message }),
}
}
pub fn run_until(
&mut self,
deadline: VirtualNanos,
on_access: &mut dyn FnMut(Access, &InterruptLevels) -> Reply,
) -> Result<Stop, BackendError> {
self.send(CoreMessage::RunUntil {
deadline_ns: deadline.0,
});
loop {
let access = match self.receive()? {
SidecarMessage::Read {
address,
width,
instruction_count,
program_counter,
virtual_time_ns,
} => Access {
kind: AccessKind::Read,
address,
width,
value: 0,
instruction_count,
virtual_time: VirtualNanos(virtual_time_ns),
program_counter,
},
SidecarMessage::Write {
address,
width,
value,
instruction_count,
program_counter,
virtual_time_ns,
} => Access {
kind: AccessKind::Write,
address,
width,
value,
instruction_count,
virtual_time: VirtualNanos(virtual_time_ns),
program_counter,
},
SidecarMessage::Stopped {
instruction_count,
virtual_time_ns,
reason,
} => {
return Ok(Stop {
at: VirtualNanos(virtual_time_ns),
instruction_count,
reason,
});
}
message @ SidecarMessage::Hello { .. } => {
return Err(BackendError::Unexpected { message });
}
};
let reply = on_access(access, &self.block.interrupt_levels);
self.send(CoreMessage::Reply {
value: reply.value,
deadline_ns: reply.next_deadline.0,
});
}
}
pub fn shutdown(&mut self) {
self.send(CoreMessage::Shutdown);
}
fn receive(&mut self) -> Result<SidecarMessage, BackendError> {
self.block.turn.wait_for(Turn::Core, &mut self.check)?;
Ok(SidecarMessage::decode(&self.block.sidecar_slot)?)
}
fn send(&self, message: CoreMessage) {
message.encode(&self.block.core_slot);
self.block.turn.hand_over(Turn::Sidecar);
}
}