2024-01-10 12:13:00 +08:00
|
|
|
use alloc::{collections::btree_map::BTreeMap, string::String, vec::Vec};
|
|
|
|
use core::mem;
|
2023-03-27 15:53:32 +08:00
|
|
|
|
2024-01-10 12:13:00 +08:00
|
|
|
use ksupport::kernel::DmaRecorder;
|
|
|
|
use libboard_artiq::{drtio_routing::RoutingTable,
|
|
|
|
drtioaux_proto::{Packet, PayloadStatus, MASTER_PAYLOAD_MAX_SIZE},
|
|
|
|
pl::csr};
|
2023-03-27 15:53:32 +08:00
|
|
|
use libcortex_a9::cache::dcci_slice;
|
2024-01-10 12:13:00 +08:00
|
|
|
use routing::{Router, Sliceable};
|
|
|
|
use subkernel::Manager as KernelManager;
|
2023-03-22 12:09:05 +08:00
|
|
|
|
|
|
|
const ALIGNMENT: usize = 64;
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
enum ManagerState {
|
|
|
|
Idle,
|
2023-03-27 15:53:32 +08:00
|
|
|
Playback,
|
2023-03-22 12:09:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct RtioStatus {
|
2023-12-12 16:51:54 +08:00
|
|
|
pub source: u8,
|
2023-03-27 15:53:32 +08:00
|
|
|
pub id: u32,
|
|
|
|
pub error: u8,
|
|
|
|
pub channel: u32,
|
|
|
|
pub timestamp: u64,
|
2023-03-22 12:09:05 +08:00
|
|
|
}
|
|
|
|
|
2024-01-10 12:13:00 +08:00
|
|
|
#[derive(Debug)]
|
2023-03-22 12:09:05 +08:00
|
|
|
pub enum Error {
|
|
|
|
IdNotFound,
|
|
|
|
PlaybackInProgress,
|
2023-03-27 15:53:32 +08:00
|
|
|
EntryNotComplete,
|
2024-01-10 12:13:00 +08:00
|
|
|
MasterDmaFound,
|
|
|
|
UploadFail,
|
2023-03-22 12:09:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct Entry {
|
|
|
|
trace: Vec<u8>,
|
|
|
|
padding_len: usize,
|
2023-03-27 15:53:32 +08:00
|
|
|
complete: bool,
|
2024-01-10 12:13:00 +08:00
|
|
|
duration: i64, // relevant for local DMA
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Entry {
|
|
|
|
pub fn from_vec(data: Vec<u8>, duration: i64) -> Entry {
|
|
|
|
let mut entry = Entry {
|
|
|
|
trace: data,
|
|
|
|
padding_len: 0,
|
|
|
|
complete: true,
|
|
|
|
duration: duration,
|
|
|
|
};
|
|
|
|
entry.realign();
|
|
|
|
entry
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn id(&self) -> u32 {
|
|
|
|
self.trace[self.padding_len..].as_ptr() as u32
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn realign(&mut self) {
|
|
|
|
self.trace.push(0);
|
|
|
|
let data_len = self.trace.len();
|
|
|
|
|
|
|
|
self.trace.reserve(ALIGNMENT - 1);
|
|
|
|
let padding = ALIGNMENT - self.trace.as_ptr() as usize % ALIGNMENT;
|
|
|
|
let padding = if padding == ALIGNMENT { 0 } else { padding };
|
|
|
|
for _ in 0..padding {
|
|
|
|
// Vec guarantees that this will not reallocate
|
|
|
|
self.trace.push(0)
|
|
|
|
}
|
|
|
|
for i in 1..data_len + 1 {
|
|
|
|
self.trace[data_len + padding - i] = self.trace[data_len - i]
|
|
|
|
}
|
|
|
|
self.complete = true;
|
|
|
|
self.padding_len = padding;
|
|
|
|
|
|
|
|
dcci_slice(&self.trace);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum RemoteTraceState {
|
|
|
|
Unsent,
|
|
|
|
Sending(usize),
|
|
|
|
Ready,
|
|
|
|
Running(usize),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct RemoteTraces {
|
|
|
|
remote_traces: BTreeMap<u8, Sliceable>,
|
|
|
|
state: RemoteTraceState,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RemoteTraces {
|
|
|
|
pub fn new(traces: BTreeMap<u8, Sliceable>) -> RemoteTraces {
|
|
|
|
RemoteTraces {
|
|
|
|
remote_traces: traces,
|
|
|
|
state: RemoteTraceState::Unsent,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// on subkernel request
|
|
|
|
pub fn upload_traces(
|
|
|
|
&mut self,
|
|
|
|
id: u32,
|
|
|
|
router: &mut Router,
|
|
|
|
rank: u8,
|
|
|
|
self_destination: u8,
|
|
|
|
routing_table: &RoutingTable,
|
|
|
|
) -> usize {
|
|
|
|
let len = self.remote_traces.len();
|
|
|
|
if len > 0 {
|
|
|
|
self.state = RemoteTraceState::Sending(self.remote_traces.len());
|
|
|
|
for (dest, trace) in self.remote_traces.iter_mut() {
|
|
|
|
// queue up the first packet for all destinations, rest will be sent after first ACK
|
|
|
|
let mut data_slice: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
|
|
|
let meta = trace.get_slice_master(&mut data_slice);
|
|
|
|
router.route(
|
|
|
|
Packet::DmaAddTraceRequest {
|
|
|
|
source: self_destination,
|
|
|
|
destination: *dest,
|
|
|
|
id: id,
|
|
|
|
status: meta.status,
|
|
|
|
length: meta.len,
|
|
|
|
trace: data_slice,
|
|
|
|
},
|
|
|
|
routing_table,
|
|
|
|
rank,
|
|
|
|
self_destination,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
len
|
|
|
|
}
|
|
|
|
|
|
|
|
// on incoming Packet::DmaAddTraceReply
|
|
|
|
pub fn ack_upload(
|
|
|
|
&mut self,
|
|
|
|
kernel_manager: &mut KernelManager,
|
|
|
|
source: u8,
|
|
|
|
id: u32,
|
|
|
|
succeeded: bool,
|
|
|
|
router: &mut Router,
|
|
|
|
rank: u8,
|
|
|
|
self_destination: u8,
|
|
|
|
routing_table: &RoutingTable,
|
|
|
|
) {
|
|
|
|
if let RemoteTraceState::Sending(count) = self.state {
|
|
|
|
if let Some(trace) = self.remote_traces.get_mut(&source) {
|
|
|
|
if trace.at_end() {
|
|
|
|
if count - 1 == 0 {
|
|
|
|
self.state = RemoteTraceState::Ready;
|
|
|
|
if let Some((id, timestamp)) = kernel_manager.ddma_remote_uploaded(succeeded) {
|
|
|
|
self.playback(id, timestamp, router, rank, self_destination, routing_table);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self.state = RemoteTraceState::Sending(count - 1);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// send next slice
|
|
|
|
let mut data_slice: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
|
|
|
|
let meta = trace.get_slice_master(&mut data_slice);
|
|
|
|
router.route(
|
|
|
|
Packet::DmaAddTraceRequest {
|
|
|
|
source: self_destination,
|
|
|
|
destination: meta.destination,
|
|
|
|
id: id,
|
|
|
|
status: meta.status,
|
|
|
|
length: meta.len,
|
|
|
|
trace: data_slice,
|
|
|
|
},
|
|
|
|
routing_table,
|
|
|
|
rank,
|
|
|
|
self_destination,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// on subkernel request
|
|
|
|
pub fn playback(
|
|
|
|
&mut self,
|
|
|
|
id: u32,
|
|
|
|
timestamp: u64,
|
|
|
|
router: &mut Router,
|
|
|
|
rank: u8,
|
|
|
|
self_destination: u8,
|
|
|
|
routing_table: &RoutingTable,
|
|
|
|
) {
|
|
|
|
// route all the playback requests
|
|
|
|
// remote traces (local trace runs on core1 unlike mainline firmware)
|
|
|
|
self.state = RemoteTraceState::Running(self.remote_traces.len());
|
|
|
|
for (dest, _) in self.remote_traces.iter() {
|
|
|
|
router.route(
|
|
|
|
Packet::DmaPlaybackRequest {
|
|
|
|
source: self_destination,
|
|
|
|
destination: *dest,
|
|
|
|
id: id,
|
|
|
|
timestamp: timestamp,
|
|
|
|
},
|
|
|
|
routing_table,
|
|
|
|
rank,
|
|
|
|
self_destination,
|
|
|
|
);
|
|
|
|
// response will be ignored (succeeded = false handled by the main thread)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// on incoming Packet::DmaPlaybackDone
|
|
|
|
pub fn remote_finished(&mut self, kernel_manager: &mut KernelManager, error: u8, channel: u32, timestamp: u64) {
|
|
|
|
if let RemoteTraceState::Running(count) = self.state {
|
|
|
|
if error != 0 || count - 1 == 0 {
|
|
|
|
// notify the kernel about a DDMA error or finish
|
|
|
|
kernel_manager.ddma_finished(error, channel, timestamp);
|
|
|
|
self.state = RemoteTraceState::Ready;
|
|
|
|
// further messages will be ignored (if there was an error)
|
|
|
|
} else {
|
|
|
|
// no error and not the last one awaited
|
|
|
|
self.state = RemoteTraceState::Running(count - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn erase(
|
|
|
|
&mut self,
|
|
|
|
id: u32,
|
|
|
|
router: &mut Router,
|
|
|
|
rank: u8,
|
|
|
|
self_destination: u8,
|
|
|
|
routing_table: &RoutingTable,
|
|
|
|
) {
|
|
|
|
for (dest, _) in self.remote_traces.iter() {
|
|
|
|
router.route(
|
|
|
|
Packet::DmaRemoveTraceRequest {
|
|
|
|
source: self_destination,
|
|
|
|
destination: *dest,
|
|
|
|
id: id,
|
|
|
|
},
|
|
|
|
routing_table,
|
|
|
|
rank,
|
|
|
|
self_destination,
|
|
|
|
);
|
|
|
|
// response will be ignored as this object will stop existing too
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn has_remote_traces(&self) -> bool {
|
|
|
|
self.remote_traces.len() > 0
|
|
|
|
}
|
2023-03-22 12:09:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Manager {
|
2023-12-12 16:51:54 +08:00
|
|
|
entries: BTreeMap<(u8, u32), Entry>,
|
2023-03-22 12:09:05 +08:00
|
|
|
state: ManagerState,
|
2023-12-12 16:51:54 +08:00
|
|
|
current_id: u32,
|
|
|
|
current_source: u8,
|
2024-01-10 12:13:00 +08:00
|
|
|
|
|
|
|
remote_entries: BTreeMap<u32, RemoteTraces>,
|
|
|
|
name_map: BTreeMap<String, u32>,
|
2023-03-22 12:09:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Manager {
|
|
|
|
pub fn new() -> Manager {
|
|
|
|
// in case Manager is created during a DMA in progress
|
|
|
|
// wait for it to end
|
2023-03-27 15:53:32 +08:00
|
|
|
unsafe { while csr::rtio_dma::enable_read() != 0 {} }
|
2023-03-22 12:09:05 +08:00
|
|
|
Manager {
|
|
|
|
entries: BTreeMap::new(),
|
2023-12-12 16:51:54 +08:00
|
|
|
current_id: 0,
|
|
|
|
current_source: 0,
|
2023-03-22 12:09:05 +08:00
|
|
|
state: ManagerState::Idle,
|
2024-01-10 12:13:00 +08:00
|
|
|
remote_entries: BTreeMap::new(),
|
|
|
|
name_map: BTreeMap::new(),
|
2023-03-22 12:09:05 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-12 16:51:54 +08:00
|
|
|
pub fn add(
|
|
|
|
&mut self,
|
|
|
|
source: u8,
|
|
|
|
id: u32,
|
|
|
|
status: PayloadStatus,
|
|
|
|
trace: &[u8],
|
|
|
|
trace_len: usize,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
let entry = match self.entries.get_mut(&(source, id)) {
|
2023-03-22 12:09:05 +08:00
|
|
|
Some(entry) => {
|
2023-11-02 14:48:52 +08:00
|
|
|
if entry.complete || status.is_first() {
|
2023-03-22 12:09:05 +08:00
|
|
|
// replace entry
|
2023-12-12 16:51:54 +08:00
|
|
|
self.entries.remove(&(source, id));
|
2023-03-27 15:53:32 +08:00
|
|
|
self.entries.insert(
|
2023-12-12 16:51:54 +08:00
|
|
|
(source, id),
|
2023-03-27 15:53:32 +08:00
|
|
|
Entry {
|
|
|
|
trace: Vec::new(),
|
|
|
|
padding_len: 0,
|
|
|
|
complete: false,
|
2024-01-10 12:13:00 +08:00
|
|
|
duration: 0,
|
2023-03-27 15:53:32 +08:00
|
|
|
},
|
|
|
|
);
|
2023-12-12 16:51:54 +08:00
|
|
|
self.entries.get_mut(&(source, id)).unwrap()
|
2023-03-22 12:09:05 +08:00
|
|
|
} else {
|
|
|
|
entry
|
|
|
|
}
|
2023-03-27 15:53:32 +08:00
|
|
|
}
|
2023-03-22 12:09:05 +08:00
|
|
|
None => {
|
2023-03-27 15:53:32 +08:00
|
|
|
self.entries.insert(
|
2023-12-12 16:51:54 +08:00
|
|
|
(source, id),
|
2023-03-27 15:53:32 +08:00
|
|
|
Entry {
|
|
|
|
trace: Vec::new(),
|
|
|
|
padding_len: 0,
|
|
|
|
complete: false,
|
2024-01-10 12:13:00 +08:00
|
|
|
duration: 0,
|
2023-03-27 15:53:32 +08:00
|
|
|
},
|
|
|
|
);
|
2023-12-12 16:51:54 +08:00
|
|
|
self.entries.get_mut(&(source, id)).unwrap()
|
2023-03-27 15:53:32 +08:00
|
|
|
}
|
2023-03-22 12:09:05 +08:00
|
|
|
};
|
|
|
|
entry.trace.extend(&trace[0..trace_len]);
|
|
|
|
|
2023-11-02 14:48:52 +08:00
|
|
|
if status.is_last() {
|
2024-01-10 12:13:00 +08:00
|
|
|
entry.realign();
|
2023-03-22 12:09:05 +08:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-01-10 12:13:00 +08:00
|
|
|
// api for DRTIO
|
2023-12-12 16:51:54 +08:00
|
|
|
pub fn erase(&mut self, source: u8, id: u32) -> Result<(), Error> {
|
|
|
|
match self.entries.remove(&(source, id)) {
|
2023-03-22 12:09:05 +08:00
|
|
|
Some(_) => Ok(()),
|
2023-03-27 15:53:32 +08:00
|
|
|
None => Err(Error::IdNotFound),
|
2023-03-22 12:09:05 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-10 12:13:00 +08:00
|
|
|
// API for subkernel
|
|
|
|
pub fn erase_name(
|
|
|
|
&mut self,
|
|
|
|
name: &str,
|
|
|
|
router: &mut Router,
|
|
|
|
rank: u8,
|
|
|
|
self_destination: u8,
|
|
|
|
routing_table: &RoutingTable,
|
|
|
|
) {
|
|
|
|
if let Some(id) = self.name_map.get(name) {
|
|
|
|
if let Some(traces) = self.remote_entries.get_mut(&id) {
|
|
|
|
traces.erase(*id, router, rank, self_destination, routing_table);
|
|
|
|
self.remote_entries.remove(&id);
|
|
|
|
}
|
|
|
|
self.entries.remove(&(self_destination, *id));
|
|
|
|
self.name_map.remove(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn remote_finished(
|
|
|
|
&mut self,
|
|
|
|
kernel_manager: &mut KernelManager,
|
|
|
|
id: u32,
|
|
|
|
error: u8,
|
|
|
|
channel: u32,
|
|
|
|
timestamp: u64,
|
|
|
|
) {
|
|
|
|
if let Some(entry) = self.remote_entries.get_mut(&id) {
|
|
|
|
entry.remote_finished(kernel_manager, error, channel, timestamp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn ack_upload(
|
|
|
|
&mut self,
|
|
|
|
kernel_manager: &mut KernelManager,
|
|
|
|
source: u8,
|
|
|
|
id: u32,
|
|
|
|
succeeded: bool,
|
|
|
|
router: &mut Router,
|
|
|
|
rank: u8,
|
|
|
|
self_destination: u8,
|
|
|
|
routing_table: &RoutingTable,
|
|
|
|
) {
|
|
|
|
if let Some(entry) = self.remote_entries.get_mut(&id) {
|
|
|
|
entry.ack_upload(
|
|
|
|
kernel_manager,
|
|
|
|
source,
|
|
|
|
id,
|
|
|
|
succeeded,
|
|
|
|
router,
|
|
|
|
rank,
|
|
|
|
self_destination,
|
|
|
|
routing_table,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// API for subkernel
|
|
|
|
pub fn upload_traces(
|
|
|
|
&mut self,
|
|
|
|
id: u32,
|
|
|
|
router: &mut Router,
|
|
|
|
rank: u8,
|
|
|
|
self_destination: u8,
|
|
|
|
routing_table: &RoutingTable,
|
|
|
|
) -> Result<usize, Error> {
|
|
|
|
let remote_traces = self.remote_entries.get_mut(&id);
|
|
|
|
let mut len = 0;
|
|
|
|
if let Some(traces) = remote_traces {
|
|
|
|
len = traces.upload_traces(id, router, rank, self_destination, routing_table);
|
|
|
|
}
|
|
|
|
Ok(len)
|
|
|
|
}
|
|
|
|
|
|
|
|
// API for subkernel
|
|
|
|
pub fn playback_remote(
|
|
|
|
&mut self,
|
|
|
|
id: u32,
|
|
|
|
timestamp: u64,
|
|
|
|
router: &mut Router,
|
|
|
|
rank: u8,
|
|
|
|
self_destination: u8,
|
|
|
|
routing_table: &RoutingTable,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
if let Some(traces) = self.remote_entries.get_mut(&id) {
|
|
|
|
traces.playback(id, timestamp, router, rank, self_destination, routing_table);
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(Error::IdNotFound)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// API for subkernel
|
|
|
|
pub fn cleanup(&mut self, router: &mut Router, rank: u8, self_destination: u8, routing_table: &RoutingTable) {
|
|
|
|
// after subkernel ends, remove all self-generated traces
|
|
|
|
for (_, id) in self.name_map.iter_mut() {
|
|
|
|
if let Some(traces) = self.remote_entries.get_mut(&id) {
|
|
|
|
traces.erase(*id, router, rank, self_destination, routing_table);
|
|
|
|
self.remote_entries.remove(&id);
|
|
|
|
}
|
|
|
|
self.entries.remove(&(self_destination, *id));
|
|
|
|
}
|
|
|
|
self.name_map.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
// API for subkernel
|
|
|
|
pub fn retrieve(&self, self_destination: u8, name: &String) -> Option<(i32, i64, bool)> {
|
|
|
|
let id = self.name_map.get(name)?;
|
|
|
|
let duration = self.entries.get(&(self_destination, *id))?.duration;
|
|
|
|
let uses_ddma = self.has_remote_traces(*id);
|
|
|
|
Some((*id as i32, duration, uses_ddma))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn has_remote_traces(&self, id: u32) -> bool {
|
|
|
|
match self.remote_entries.get(&id) {
|
|
|
|
Some(traces) => traces.has_remote_traces(),
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn put_record(&mut self, mut recorder: DmaRecorder, self_destination: u8) -> Result<u32, Error> {
|
|
|
|
let mut remote_traces: BTreeMap<u8, Sliceable> = BTreeMap::new();
|
|
|
|
|
|
|
|
let mut local_trace: Vec<u8> = Vec::new();
|
|
|
|
// analyze each entry and put in proper buckets, as the kernel core
|
|
|
|
// sends whole chunks, to limit comms/kernel CPU communication,
|
|
|
|
// and as only comms core has access to varios DMA buffers.
|
|
|
|
let mut ptr = 0;
|
|
|
|
recorder.buffer.push(0);
|
|
|
|
while recorder.buffer[ptr] != 0 {
|
|
|
|
// ptr + 3 = tgt >> 24 (destination)
|
|
|
|
let len = recorder.buffer[ptr] as usize;
|
|
|
|
let destination = recorder.buffer[ptr + 3];
|
|
|
|
if destination == 0 {
|
|
|
|
return Err(Error::MasterDmaFound);
|
|
|
|
} else if destination == self_destination {
|
|
|
|
local_trace.extend(&recorder.buffer[ptr..ptr + len]);
|
|
|
|
} else {
|
|
|
|
if let Some(remote_trace) = remote_traces.get_mut(&destination) {
|
|
|
|
remote_trace.extend(&recorder.buffer[ptr..ptr + len]);
|
|
|
|
} else {
|
|
|
|
remote_traces.insert(
|
|
|
|
destination,
|
|
|
|
Sliceable::new(destination, recorder.buffer[ptr..ptr + len].to_vec()),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// and jump to the next event
|
|
|
|
ptr += len;
|
|
|
|
}
|
|
|
|
let local_entry = Entry::from_vec(local_trace, recorder.duration);
|
|
|
|
|
|
|
|
let id = local_entry.id();
|
|
|
|
self.entries.insert((self_destination, id), local_entry);
|
|
|
|
self.remote_entries.insert(id, RemoteTraces::new(remote_traces));
|
|
|
|
let mut name = String::new();
|
|
|
|
mem::swap(&mut recorder.name, &mut name);
|
|
|
|
self.name_map.insert(name, id);
|
|
|
|
|
|
|
|
Ok(id)
|
|
|
|
}
|
|
|
|
|
2023-12-12 16:51:54 +08:00
|
|
|
pub fn playback(&mut self, source: u8, id: u32, timestamp: u64) -> Result<(), Error> {
|
2023-03-22 12:09:05 +08:00
|
|
|
if self.state != ManagerState::Idle {
|
|
|
|
return Err(Error::PlaybackInProgress);
|
|
|
|
}
|
|
|
|
|
2023-12-12 16:51:54 +08:00
|
|
|
let entry = match self.entries.get(&(source, id)) {
|
2023-03-22 12:09:05 +08:00
|
|
|
Some(entry) => entry,
|
2023-03-27 15:53:32 +08:00
|
|
|
None => {
|
|
|
|
return Err(Error::IdNotFound);
|
|
|
|
}
|
2023-03-22 12:09:05 +08:00
|
|
|
};
|
|
|
|
if !entry.complete {
|
|
|
|
return Err(Error::EntryNotComplete);
|
|
|
|
}
|
|
|
|
let ptr = entry.trace[entry.padding_len..].as_ptr();
|
|
|
|
assert!(ptr as u32 % 64 == 0);
|
|
|
|
|
|
|
|
self.state = ManagerState::Playback;
|
2023-12-12 16:51:54 +08:00
|
|
|
self.current_id = id;
|
|
|
|
self.current_source = source;
|
2023-03-22 12:09:05 +08:00
|
|
|
|
|
|
|
unsafe {
|
|
|
|
csr::rtio_dma::base_address_write(ptr as u32);
|
|
|
|
csr::rtio_dma::time_offset_write(timestamp as u64);
|
2023-03-27 15:53:32 +08:00
|
|
|
|
2023-03-22 12:09:05 +08:00
|
|
|
csr::cri_con::selected_write(1);
|
|
|
|
csr::rtio_dma::enable_write(1);
|
|
|
|
// playback has begun here, for status call check_state
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn check_state(&mut self) -> Option<RtioStatus> {
|
|
|
|
if self.state != ManagerState::Playback {
|
|
|
|
// nothing to report
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let dma_enable = unsafe { csr::rtio_dma::enable_read() };
|
|
|
|
if dma_enable != 0 {
|
|
|
|
return None;
|
|
|
|
} else {
|
|
|
|
self.state = ManagerState::Idle;
|
2023-03-27 15:53:32 +08:00
|
|
|
unsafe {
|
2023-03-22 12:09:05 +08:00
|
|
|
csr::cri_con::selected_write(0);
|
2023-03-27 15:53:32 +08:00
|
|
|
let error = csr::rtio_dma::error_read();
|
2023-03-22 12:09:05 +08:00
|
|
|
let channel = csr::rtio_dma::error_channel_read();
|
|
|
|
let timestamp = csr::rtio_dma::error_timestamp_read();
|
|
|
|
if error != 0 {
|
|
|
|
csr::rtio_dma::error_write(1);
|
|
|
|
}
|
2023-03-27 15:53:32 +08:00
|
|
|
return Some(RtioStatus {
|
2023-12-12 16:51:54 +08:00
|
|
|
source: self.current_source,
|
|
|
|
id: self.current_id,
|
2023-03-22 12:09:05 +08:00
|
|
|
error: error,
|
2023-03-27 15:53:32 +08:00
|
|
|
channel: channel,
|
|
|
|
timestamp: timestamp,
|
|
|
|
});
|
2023-03-22 12:09:05 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-09-06 16:06:38 +08:00
|
|
|
|
|
|
|
pub fn running(&self) -> bool {
|
|
|
|
self.state == ManagerState::Playback
|
|
|
|
}
|
2023-03-27 15:53:32 +08:00
|
|
|
}
|