artiq-zynq/src/satman/src/dma.rs

178 lines
5.0 KiB
Rust
Raw Normal View History

2023-03-27 15:53:32 +08:00
use alloc::{collections::btree_map::BTreeMap, vec::Vec};
use libboard_artiq::{drtioaux_proto::PayloadStatus, pl::csr};
2023-03-27 15:53:32 +08:00
use libcortex_a9::cache::dcci_slice;
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-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
}
pub enum Error {
IdNotFound,
PlaybackInProgress,
2023-03-27 15:53:32 +08:00
EntryNotComplete,
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,
2023-03-22 12:09:05 +08:00
}
#[derive(Debug)]
pub struct Manager {
entries: BTreeMap<u32, Entry>,
state: ManagerState,
2023-03-27 15:53:32 +08:00
currentid: 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(),
currentid: 0,
state: ManagerState::Idle,
}
}
pub fn add(&mut self, id: u32, status: PayloadStatus, trace: &[u8], trace_len: usize) -> Result<(), Error> {
2023-03-22 12:09:05 +08:00
let entry = match self.entries.get_mut(&id) {
Some(entry) => {
if entry.complete || status.is_first() {
2023-03-22 12:09:05 +08:00
// replace entry
self.entries.remove(&id);
2023-03-27 15:53:32 +08:00
self.entries.insert(
id,
Entry {
trace: Vec::new(),
padding_len: 0,
complete: false,
},
);
2023-03-22 12:09:05 +08:00
self.entries.get_mut(&id).unwrap()
} 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(
id,
Entry {
trace: Vec::new(),
padding_len: 0,
complete: false,
},
);
2023-03-22 12:09:05 +08:00
self.entries.get_mut(&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]);
if status.is_last() {
2023-03-22 12:09:05 +08:00
entry.trace.push(0);
let data_len = entry.trace.len();
2023-03-27 15:53:32 +08:00
2023-03-22 12:09:05 +08:00
// Realign.
entry.trace.reserve(ALIGNMENT - 1);
let padding = ALIGNMENT - entry.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
entry.trace.push(0)
}
for i in 1..data_len + 1 {
entry.trace[data_len + padding - i] = entry.trace[data_len - i]
}
entry.complete = true;
entry.padding_len = padding;
dcci_slice(&entry.trace);
}
Ok(())
}
pub fn erase(&mut self, id: u32) -> Result<(), Error> {
match self.entries.remove(&id) {
Some(_) => Ok(()),
2023-03-27 15:53:32 +08:00
None => Err(Error::IdNotFound),
2023-03-22 12:09:05 +08:00
}
}
pub fn playback(&mut self, id: u32, timestamp: u64) -> Result<(), Error> {
if self.state != ManagerState::Idle {
return Err(Error::PlaybackInProgress);
}
2023-03-27 15:53:32 +08:00
let entry = match self.entries.get(&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;
self.currentid = id;
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 {
id: self.currentid,
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
}