diff --git a/artiq/firmware/libproto_artiq/drtioaux_proto.rs b/artiq/firmware/libproto_artiq/drtioaux_proto.rs
index 94f26dca7..4a962f6fe 100644
--- a/artiq/firmware/libproto_artiq/drtioaux_proto.rs
+++ b/artiq/firmware/libproto_artiq/drtioaux_proto.rs
@@ -14,8 +14,11 @@ impl<T> From<IoError<T>> for Error<T> {
     }
 }
 
-pub const DMA_TRACE_MAX_SIZE: usize = /*max size*/512 - /*CRC*/4 - /*packet ID*/1 - /*trace ID*/4 - /*last*/1 -/*length*/2;
-pub const ANALYZER_MAX_SIZE: usize  = /*max size*/512 - /*CRC*/4 - /*packet ID*/1 - /*last*/1 - /*length*/2;
+// maximum size of arbitrary payloads
+// used by satellite -> master analyzer, subkernel exceptions
+pub const SAT_PAYLOAD_MAX_SIZE: usize  = /*max size*/512 - /*CRC*/4 - /*packet ID*/1 - /*last*/1 - /*length*/2;
+// used by DDMA, subkernel program data (need to provide extra ID and destination)
+pub const MASTER_PAYLOAD_MAX_SIZE: usize = SAT_PAYLOAD_MAX_SIZE - /*destination*/1 - /*ID*/4;
 
 #[derive(PartialEq, Debug)]
 pub enum Packet {
@@ -61,9 +64,9 @@ pub enum Packet {
     AnalyzerHeaderRequest { destination: u8 },
     AnalyzerHeader { sent_bytes: u32, total_byte_count: u64, overflow_occurred: bool },
     AnalyzerDataRequest { destination: u8 },
-    AnalyzerData { last: bool, length: u16, data: [u8; ANALYZER_MAX_SIZE]},
+    AnalyzerData { last: bool, length: u16, data: [u8; SAT_PAYLOAD_MAX_SIZE]},
 
-    DmaAddTraceRequest { destination: u8, id: u32, last: bool, length: u16, trace: [u8; DMA_TRACE_MAX_SIZE] },
+    DmaAddTraceRequest { destination: u8, id: u32, last: bool, length: u16, trace: [u8; MASTER_PAYLOAD_MAX_SIZE] },
     DmaAddTraceReply { succeeded: bool },
     DmaRemoveTraceRequest { destination: u8, id: u32 },
     DmaRemoveTraceReply { succeeded: bool },
@@ -215,7 +218,7 @@ impl Packet {
             0xa3 => {
                 let last = reader.read_bool()?;
                 let length = reader.read_u16()?;
-                let mut data: [u8; ANALYZER_MAX_SIZE] = [0; ANALYZER_MAX_SIZE];
+                let mut data: [u8; SAT_PAYLOAD_MAX_SIZE] = [0; SAT_PAYLOAD_MAX_SIZE];
                 reader.read_exact(&mut data[0..length as usize])?;
                 Packet::AnalyzerData {
                     last: last,
@@ -229,7 +232,7 @@ impl Packet {
                 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];
+                let mut trace: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
                 reader.read_exact(&mut trace[0..length as usize])?;
                 Packet::DmaAddTraceRequest {
                     destination: destination,
diff --git a/artiq/firmware/runtime/rtio_mgt.rs b/artiq/firmware/runtime/rtio_mgt.rs
index 3ead188b5..f643fafd5 100644
--- a/artiq/firmware/runtime/rtio_mgt.rs
+++ b/artiq/firmware/runtime/rtio_mgt.rs
@@ -17,7 +17,7 @@ pub mod drtio {
     use super::*;
     use alloc::vec::Vec;
     use drtioaux;
-    use proto_artiq::drtioaux_proto::DMA_TRACE_MAX_SIZE;
+    use proto_artiq::drtioaux_proto::MASTER_PAYLOAD_MAX_SIZE;
     use rtio_dma::remote_dma;
     #[cfg(has_rtio_analyzer)]
     use analyzer::remote_analyzer::RemoteBuffer;
@@ -372,28 +372,36 @@ pub mod drtio {
         }
     }
 
-    pub fn ddma_upload_trace(io: &Io, aux_mutex: &Mutex, routing_table: &drtio_routing::RoutingTable,
-            id: u32, destination: u8, trace: &Vec<u8>) -> Result<(), &'static str> {
-        let linkno = routing_table.0[destination as usize][0] - 1;
+    fn partition_data<F>(data: &[u8], send_f: F) -> Result<(), &'static str>
+            where F: Fn(&[u8; MASTER_PAYLOAD_MAX_SIZE], bool, usize) -> Result<(), &'static str> {
         let mut i = 0;
-        while i < trace.len() {
-            let mut trace_slice: [u8; DMA_TRACE_MAX_SIZE] = [0; DMA_TRACE_MAX_SIZE];
-            let len: usize = if i + DMA_TRACE_MAX_SIZE < trace.len() { DMA_TRACE_MAX_SIZE } else { trace.len() - i } as usize;
-            let last = i + len == trace.len();
-            trace_slice[..len].clone_from_slice(&trace[i..i+len]);
+            while i < data.len() {
+                let mut slice: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE];
+                let len: usize = if i + MASTER_PAYLOAD_MAX_SIZE < data.len() { MASTER_PAYLOAD_MAX_SIZE } else { data.len() - i } as usize;
+                let last = i + len == data.len();
+                slice[..len].clone_from_slice(&data[i..i+len]);
             i += len;
+                send_f(&slice, last, len)?;
+            }
+            Ok(())
+        }
+
+    pub fn ddma_upload_trace(io: &Io, aux_mutex: &Mutex,
+            routing_table: &drtio_routing::RoutingTable,
+            id: u32, destination: u8, trace: &[u8]) -> Result<(), &'static str> {
+        let linkno = routing_table.0[destination as usize][0] - 1;
+        partition_data(trace, |slice, last, len: usize| {
             let reply = aux_transact(io, aux_mutex, linkno, 
                 &drtioaux::Packet::DmaAddTraceRequest {
-                    id: id, destination: destination, last: last, length: len as u16, trace: trace_slice});
+                    id: id, destination: destination, last: last, length: len as u16, trace: *slice});
             match reply {
-                Ok(drtioaux::Packet::DmaAddTraceReply { succeeded: true }) => (),
-                Ok(drtioaux::Packet::DmaAddTraceReply { succeeded: false }) => { 
-                    return Err("error adding trace on satellite"); },
-                Ok(_) => { return Err("adding DMA trace failed, unexpected aux packet"); },
-                Err(_) => { return Err("adding DMA trace failed, aux error"); }
+                Ok(drtioaux::Packet::DmaAddTraceReply { succeeded: true }) => Ok(()),
+                Ok(drtioaux::Packet::DmaAddTraceReply { succeeded: false }) =>  
+                    Err("error adding trace on satellite"),
+                Ok(_) => Err("adding DMA trace failed, unexpected aux packet"),
+                Err(_) => Err("adding DMA trace failed, aux error")
             }
-        }
-        Ok(())
+        })
     }
 
     pub fn ddma_send_erase(io: &Io, aux_mutex: &Mutex, routing_table: &drtio_routing::RoutingTable, 
diff --git a/artiq/firmware/satman/analyzer.rs b/artiq/firmware/satman/analyzer.rs
index e2a43ba22..72f9e8c2b 100644
--- a/artiq/firmware/satman/analyzer.rs
+++ b/artiq/firmware/satman/analyzer.rs
@@ -1,6 +1,6 @@
 use core::cmp::min;
 use board_misoc::{csr, cache};
-use proto_artiq::drtioaux_proto::ANALYZER_MAX_SIZE;
+use proto_artiq::drtioaux_proto::SAT_PAYLOAD_MAX_SIZE;
 
 const BUFFER_SIZE: usize = 512 * 1024;
 
@@ -86,10 +86,10 @@ impl Analyzer {
         }
     }
 
-    pub fn get_data(&mut self, data_slice: &mut [u8; ANALYZER_MAX_SIZE]) -> AnalyzerSliceMeta {
+    pub fn get_data(&mut self, data_slice: &mut [u8; SAT_PAYLOAD_MAX_SIZE]) -> AnalyzerSliceMeta {
         let data = unsafe { &BUFFER.data[..] };
         let i = (self.data_pointer + self.sent_bytes) % BUFFER_SIZE;
-        let len = min(ANALYZER_MAX_SIZE, self.data_len - self.sent_bytes);
+        let len = min(SAT_PAYLOAD_MAX_SIZE, self.data_len - self.sent_bytes);
         let last = self.sent_bytes + len == self.data_len;
 
         if i + len >= BUFFER_SIZE {
diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs
index c524483fc..4384e68d3 100644
--- a/artiq/firmware/satman/main.rs
+++ b/artiq/firmware/satman/main.rs
@@ -17,8 +17,7 @@ use board_artiq::si5324;
 use board_artiq::{spi, drtioaux};
 #[cfg(soc_platform = "efc")]
 use board_artiq::ad9117;
-use board_artiq::drtio_routing;
-use proto_artiq::drtioaux_proto::ANALYZER_MAX_SIZE;
+use proto_artiq::drtioaux_proto::{SAT_PAYLOAD_MAX_SIZE, MASTER_PAYLOAD_MAX_SIZE};
 #[cfg(has_drtio_eem)]
 use board_artiq::drtio_eem;
 use riscv::register::{mcause, mepc, mtval};
@@ -328,7 +327,7 @@ fn process_aux_packet(_manager: &mut DmaManager, analyzer: &mut Analyzer, _repea
 
         drtioaux::Packet::AnalyzerDataRequest { destination: _destination } => {
             forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
-            let mut data_slice: [u8; ANALYZER_MAX_SIZE] = [0; ANALYZER_MAX_SIZE];
+            let mut data_slice: [u8; SAT_PAYLOAD_MAX_SIZE] = [0; SAT_PAYLOAD_MAX_SIZE];
             let meta = analyzer.get_data(&mut data_slice);
             drtioaux::send(0, &drtioaux::Packet::AnalyzerData {
                 last: meta.last,