forked from M-Labs/artiq
1
0
Fork 0

satman: distributed DMA support

This commit is contained in:
Spaqin 2023-03-11 18:36:36 +08:00 committed by GitHub
parent 15c18bdc81
commit 84e7515721
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 290 additions and 5 deletions

View File

@ -347,6 +347,7 @@ dependencies = [
name = "satman" name = "satman"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"alloc_list",
"board_artiq", "board_artiq",
"board_misoc", "board_misoc",
"build_misoc", "build_misoc",

View File

@ -14,6 +14,9 @@ impl<T> From<IoError<T>> for Error<T> {
} }
} }
/* 512 (max size) - 4 (CRC) - 1 (packet ID) - 4 (trace ID) - 1 (last) - 2 (length) */
const DMA_TRACE_MAX_SIZE: usize = 500;
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
pub enum Packet { pub enum Packet {
EchoRequest, EchoRequest,
@ -54,6 +57,15 @@ pub enum Packet {
SpiReadRequest { destination: u8, busno: u8 }, SpiReadRequest { destination: u8, busno: u8 },
SpiReadReply { succeeded: bool, data: u32 }, SpiReadReply { succeeded: bool, data: u32 },
SpiBasicReply { succeeded: bool }, SpiBasicReply { succeeded: bool },
DmaAddTraceRequest { id: u32, last: bool, length: u16, trace: [u8; DMA_TRACE_MAX_SIZE] },
DmaAddTraceReply { succeeded: bool },
DmaRemoveTraceRequest { id: u32 },
DmaRemoveTraceReply { succeeded: bool },
DmaPlaybackRequest { id: u32, timestamp: u64 },
DmaPlaybackReply { succeeded: bool },
DmaPlaybackStatus { id: u32, error: u8, channel: u32, timestamp: u64 }
} }
impl Packet { impl Packet {
@ -185,6 +197,42 @@ impl Packet {
succeeded: reader.read_bool()? succeeded: reader.read_bool()?
}, },
0xb0 => {
let id = reader.read_u32()?;
let last = reader.read_bool()?;
let length = reader.read_u16()?;
let mut trace: [u8; DMA_TRACE_MAX_SIZE] = [0; DMA_TRACE_MAX_SIZE];
reader.read_exact(&mut trace[0..length as usize])?;
Packet::DmaAddTraceRequest {
id: id,
last: last,
length: length as u16,
trace: trace,
}
},
0xb1 => Packet::DmaAddTraceReply {
succeeded: reader.read_bool()?
},
0xb2 => Packet::DmaRemoveTraceRequest {
id: reader.read_u32()?
},
0xb3 => Packet::DmaRemoveTraceReply {
succeeded: reader.read_bool()?
},
0xb4 => Packet::DmaPlaybackRequest {
id: reader.read_u32()?,
timestamp: reader.read_u64()?
},
0xb5 => Packet::DmaPlaybackReply {
succeeded: reader.read_bool()?
},
0xb6 => Packet::DmaPlaybackStatus {
id: reader.read_u32()?,
error: reader.read_u8()?,
channel: reader.read_u32()?,
timestamp: reader.read_u64()?
},
ty => return Err(Error::UnknownPacket(ty)) ty => return Err(Error::UnknownPacket(ty))
}) })
} }
@ -343,6 +391,44 @@ impl Packet {
writer.write_u8(0x95)?; writer.write_u8(0x95)?;
writer.write_bool(succeeded)?; writer.write_bool(succeeded)?;
}, },
Packet::DmaAddTraceRequest { id, last, trace, length } => {
writer.write_u8(0xb0)?;
writer.write_u32(id)?;
writer.write_bool(last)?;
// trace may be broken down to fit within drtio aux memory limit
// will be reconstructed by satellite
writer.write_u16(length)?;
writer.write_all(&trace[0..length as usize])?;
},
Packet::DmaAddTraceReply { succeeded } => {
writer.write_u8(0xb1)?;
writer.write_bool(succeeded)?;
},
Packet::DmaRemoveTraceRequest { id } => {
writer.write_u8(0xb2)?;
writer.write_u32(id)?;
},
Packet::DmaRemoveTraceReply { succeeded } => {
writer.write_u8(0xb3)?;
writer.write_bool(succeeded)?;
},
Packet::DmaPlaybackRequest { id, timestamp } => {
writer.write_u8(0xb4)?;
writer.write_u32(id)?;
writer.write_u64(timestamp)?;
},
Packet::DmaPlaybackReply { succeeded } => {
writer.write_u8(0xb5)?;
writer.write_bool(succeeded)?;
},
Packet::DmaPlaybackStatus { id, error, channel, timestamp } => {
writer.write_u8(0xb6)?;
writer.write_u32(id)?;
writer.write_u8(error)?;
writer.write_u32(channel)?;
writer.write_u64(timestamp)?;
}
} }
Ok(()) Ok(())
} }

View File

@ -16,4 +16,5 @@ build_misoc = { path = "../libbuild_misoc" }
log = { version = "0.4", default-features = false } log = { version = "0.4", default-features = false }
board_misoc = { path = "../libboard_misoc", features = ["uart_console", "log"] } board_misoc = { path = "../libboard_misoc", features = ["uart_console", "log"] }
board_artiq = { path = "../libboard_artiq" } board_artiq = { path = "../libboard_artiq" }
alloc_list = { path = "../liballoc_list" }
riscv = { version = "0.6.0", features = ["inline-asm"] } riscv = { version = "0.6.0", features = ["inline-asm"] }

View File

@ -0,0 +1,151 @@
use board_misoc::csr;
use alloc::{vec::Vec, collections::btree_map::BTreeMap};
const ALIGNMENT: usize = 64;
#[derive(Debug, PartialEq)]
enum ManagerState {
Idle,
Playback
}
pub struct RtioStatus {
pub id: u32,
pub error: u8,
pub channel: u32,
pub timestamp: u64
}
pub enum Error {
IdNotFound,
PlaybackInProgress,
EntryNotComplete
}
#[derive(Debug)]
struct Entry {
trace: Vec<u8>,
padding_len: usize,
complete: bool
}
#[derive(Debug)]
pub struct Manager {
entries: BTreeMap<u32, Entry>,
state: ManagerState,
currentid: u32
}
impl Manager {
pub fn new() -> Manager {
// in case Manager is created during a DMA in progress
// wait for it to end
unsafe {
while csr::rtio_dma::enable_read() != 0 {}
}
Manager {
entries: BTreeMap::new(),
currentid: 0,
state: ManagerState::Idle,
}
}
pub fn add(&mut self, id: u32, last: bool, trace: &[u8], trace_len: usize) -> Result<(), Error> {
let entry = match self.entries.get_mut(&id) {
Some(entry) => entry,
None => {
self.entries.insert(id, Entry {
trace: Vec::new(),
padding_len: 0,
complete: false });
self.entries.get_mut(&id).unwrap()
},
};
entry.trace.extend(&trace[0..trace_len]);
if last {
entry.trace.push(0);
let data_len = entry.trace.len();
// 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;
}
Ok(())
}
pub fn erase(&mut self, id: u32) -> Result<(), Error> {
match self.entries.remove(&id) {
Some(_) => Ok(()),
None => Err(Error::IdNotFound)
}
}
pub fn playback(&mut self, id: u32, timestamp: u64) -> Result<(), Error> {
if self.state != ManagerState::Idle {
return Err(Error::PlaybackInProgress);
}
let entry = match self.entries.get(&id){
Some(entry) => entry,
None => { return Err(Error::IdNotFound); }
};
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 u64);
csr::rtio_dma::time_offset_write(timestamp as u64);
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;
unsafe {
csr::cri_con::selected_write(0);
let error = csr::rtio_dma::error_read();
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);
}
return Some(RtioStatus {
id: self.currentid,
error: error,
channel: channel,
timestamp: timestamp });
}
}
}
}

View File

@ -1,4 +1,4 @@
#![feature(never_type, panic_info_message, llvm_asm)] #![feature(never_type, panic_info_message, llvm_asm, default_alloc_error_handler)]
#![no_std] #![no_std]
#[macro_use] #[macro_use]
@ -7,6 +7,7 @@ extern crate log;
extern crate board_misoc; extern crate board_misoc;
extern crate board_artiq; extern crate board_artiq;
extern crate riscv; extern crate riscv;
extern crate alloc;
use core::convert::TryFrom; use core::convert::TryFrom;
use board_misoc::{csr, ident, clock, uart_logger, i2c, pmp}; use board_misoc::{csr, ident, clock, uart_logger, i2c, pmp};
@ -15,8 +16,13 @@ use board_artiq::si5324;
use board_artiq::{spi, drtioaux}; use board_artiq::{spi, drtioaux};
use board_artiq::drtio_routing; use board_artiq::drtio_routing;
use riscv::register::{mcause, mepc, mtval}; use riscv::register::{mcause, mepc, mtval};
use dma::Manager as DmaManager;
#[global_allocator]
static mut ALLOC: alloc_list::ListAlloc = alloc_list::EMPTY;
mod repeater; mod repeater;
mod dma;
fn drtiosat_reset(reset: bool) { fn drtiosat_reset(reset: bool) {
unsafe { unsafe {
@ -67,7 +73,7 @@ macro_rules! forward {
($routing_table:expr, $destination:expr, $rank:expr, $repeaters:expr, $packet:expr) => {} ($routing_table:expr, $destination:expr, $rank:expr, $repeaters:expr, $packet:expr) => {}
} }
fn process_aux_packet(_repeaters: &mut [repeater::Repeater], fn process_aux_packet(_manager: &mut DmaManager, _repeaters: &mut [repeater::Repeater],
_routing_table: &mut drtio_routing::RoutingTable, _rank: &mut u8, _routing_table: &mut drtio_routing::RoutingTable, _rank: &mut u8,
packet: drtioaux::Packet) -> Result<(), drtioaux::Error<!>> { packet: drtioaux::Packet) -> Result<(), drtioaux::Error<!>> {
// In the code below, *_chan_sel_write takes an u8 if there are fewer than 256 channels, // In the code below, *_chan_sel_write takes an u8 if there are fewer than 256 channels,
@ -294,6 +300,24 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
&drtioaux::Packet::SpiReadReply { succeeded: false, data: 0 }) &drtioaux::Packet::SpiReadReply { succeeded: false, data: 0 })
} }
} }
#[cfg(has_rtio_dma)]
drtioaux::Packet::DmaAddTraceRequest { id, last, length, trace } => {
let succeeded = _manager.add(id, last, &trace, length as usize).is_ok();
drtioaux::send(0,
&drtioaux::Packet::DmaAddTraceReply { succeeded: succeeded })
}
#[cfg(has_rtio_dma)]
drtioaux::Packet::DmaRemoveTraceRequest { id } => {
let succeeded = _manager.erase(id).is_ok();
drtioaux::send(0,
&drtioaux::Packet::DmaRemoveTraceReply { succeeded: succeeded })
}
#[cfg(has_rtio_dma)]
drtioaux::Packet::DmaPlaybackRequest { id, timestamp } => {
let succeeded = _manager.playback(id, timestamp).is_ok();
drtioaux::send(0,
&drtioaux::Packet::DmaPlaybackReply { succeeded: succeeded })
}
_ => { _ => {
warn!("received unexpected aux packet"); warn!("received unexpected aux packet");
@ -302,12 +326,12 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
} }
} }
fn process_aux_packets(repeaters: &mut [repeater::Repeater], fn process_aux_packets(dma_manager: &mut DmaManager, repeaters: &mut [repeater::Repeater],
routing_table: &mut drtio_routing::RoutingTable, rank: &mut u8) { routing_table: &mut drtio_routing::RoutingTable, rank: &mut u8) {
let result = let result =
drtioaux::recv(0).and_then(|packet| { drtioaux::recv(0).and_then(|packet| {
if let Some(packet) = packet { if let Some(packet) = packet {
process_aux_packet(repeaters, routing_table, rank, packet) process_aux_packet(dma_manager, repeaters, routing_table, rank, packet)
} else { } else {
Ok(()) Ok(())
} }
@ -432,10 +456,13 @@ fn sysclk_setup() {
#[no_mangle] #[no_mangle]
pub extern fn main() -> i32 { pub extern fn main() -> i32 {
extern { extern {
static mut _fheap: u8;
static mut _eheap: u8;
static mut _sstack_guard: u8; static mut _sstack_guard: u8;
} }
unsafe { unsafe {
ALLOC.add_range(&mut _fheap, &mut _eheap);
pmp::init_stack_guard(&_sstack_guard as *const u8 as usize); pmp::init_stack_guard(&_sstack_guard as *const u8 as usize);
} }
@ -511,13 +538,18 @@ pub extern fn main() -> i32 {
si5324::siphaser::calibrate_skew().expect("failed to calibrate skew"); si5324::siphaser::calibrate_skew().expect("failed to calibrate skew");
} }
// DMA manager created here, so when link is dropped, all DMA traces
// are cleared out for a clean slate on subsequent connections,
// without a manual intervention.
let mut dma_manager = DmaManager::new();
drtioaux::reset(0); drtioaux::reset(0);
drtiosat_reset(false); drtiosat_reset(false);
drtiosat_reset_phy(false); drtiosat_reset_phy(false);
while drtiosat_link_rx_up() { while drtiosat_link_rx_up() {
drtiosat_process_errors(); drtiosat_process_errors();
process_aux_packets(&mut repeaters, &mut routing_table, &mut rank); process_aux_packets(&mut dma_manager, &mut repeaters, &mut routing_table, &mut rank);
for rep in repeaters.iter_mut() { for rep in repeaters.iter_mut() {
rep.service(&routing_table, rank); rep.service(&routing_table, rank);
} }
@ -538,6 +570,12 @@ pub extern fn main() -> i32 {
error!("aux packet error: {}", e); error!("aux packet error: {}", e);
} }
} }
if let Some(status) = dma_manager.check_state() {
if let Err(e) = drtioaux::send(0, &drtioaux::Packet::DmaPlaybackStatus {
id: status.id, error: status.error, channel: status.channel, timestamp: status.timestamp }) {
error!("error sending DMA playback status: {}", e);
}
}
} }
drtiosat_reset_phy(true); drtiosat_reset_phy(true);

View File

@ -66,4 +66,12 @@ SECTIONS
. += 0x10000; . += 0x10000;
_fstack = . - 16; _fstack = . - 16;
} > main_ram } > main_ram
/* 64MB heap for alloc use */
.heap (NOLOAD) : ALIGN(16)
{
_fheap = .;
. = . + 0x4000000;
_eheap = .;
} > main_ram
} }