#[cfg(feature = "trace")]
mod imp {
pub const SYNC0: u8 = 0xA5;
pub const SYNC1: u8 = 0x5A;
pub const PROTOCOL_VERSION: u8 = 1;
pub const NO_TASK: u32 = u32::MAX;
pub const MAX_PAYLOAD: usize = 32;
pub const FRAME_OVERHEAD: usize = 2 + 1 + 2 + 2 + 1 + 4 + 8 + 1 + 2;
pub const MAX_FRAME: usize = FRAME_OVERHEAD + MAX_PAYLOAD;
pub fn crc16(data: &[u8]) -> u16 {
let mut crc: u16 = 0xFFFF;
for &byte in data {
crc ^= (byte as u16) << 8;
for _ in 0..8 {
if crc & 0x8000 != 0 {
crc = (crc << 1) ^ 0x1021;
} else {
crc <<= 1;
}
}
}
crc
}
use core::sync::atomic::{AtomicU16, Ordering};
static SEQ: AtomicU16 = AtomicU16::new(0);
pub fn encode(
buf: &mut [u8; MAX_FRAME],
kind: u16,
core_id: u8,
task_id: u32,
timestamp: u64,
payload: &[u8],
) -> usize {
debug_assert!(payload.len() <= MAX_PAYLOAD);
let seq = SEQ.fetch_add(1, Ordering::Relaxed);
buf[0] = SYNC0;
buf[1] = SYNC1;
buf[2] = PROTOCOL_VERSION;
buf[3..5].copy_from_slice(&seq.to_le_bytes());
buf[5..7].copy_from_slice(&kind.to_le_bytes());
buf[7] = core_id;
buf[8..12].copy_from_slice(&task_id.to_le_bytes());
buf[12..20].copy_from_slice(×tamp.to_le_bytes());
buf[20] = payload.len() as u8;
buf[21..21 + payload.len()].copy_from_slice(payload);
let crc_end = 21 + payload.len();
let crc = crc16(&buf[0..crc_end]);
buf[crc_end..crc_end + 2].copy_from_slice(&crc.to_le_bytes());
crc_end + 2
}
pub fn now_ts() -> u64 {
crate::port::board::now_us()
}
pub fn core_id() -> u8 {
crate::port::arch::hart_id() as u8
}
}
#[cfg(feature = "trace")]
use imp::*;
#[cfg(feature = "trace")]
mod kind {
pub const STREAM_HEADER: u16 = 0x0701;
pub const TASK_CREATED: u16 = 0x0001;
pub const CONTEXT_SWITCH: u16 = 0x0009;
pub const IRQ_ENTER: u16 = 0x0301;
pub const IRQ_EXIT: u16 = 0x0302;
pub const MUTEX_LOCK_ACQUIRED: u16 = 0x0102;
pub const MUTEX_UNLOCK: u16 = 0x0104;
pub const PRIORITY_INHERIT: u16 = 0x0105;
pub const HARD_FAULT: u16 = 0x0601;
pub const STACK_OVERFLOW: u16 = 0x0605;
}
#[cfg(feature = "trace")]
#[derive(Clone, Copy)]
pub enum SwitchReason {
Preempted = 0,
TimerWake = 6,
}
#[cfg(feature = "trace")]
static CACHED_CPU_HZ: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
#[cfg(feature = "trace")]
static CACHED_MAX_HARTS: core::sync::atomic::AtomicU8 = core::sync::atomic::AtomicU8::new(0);
#[cfg(feature = "trace")]
pub fn stream_header(cpu_hz: u32, max_harts: u8) {
use core::sync::atomic::Ordering;
CACHED_CPU_HZ.store(cpu_hz, Ordering::Relaxed);
CACHED_MAX_HARTS.store(max_harts, Ordering::Relaxed);
let mut payload = [0u8; 8];
payload[0] = 0; payload[1] = 1;
payload[2] = 0;
payload[3..7].copy_from_slice(&cpu_hz.to_le_bytes());
payload[7] = max_harts;
let mut buf = [0u8; MAX_FRAME];
let n = encode(&mut buf, kind::STREAM_HEADER, core_id(), NO_TASK, now_ts(), &payload);
crate::port::board::trace_write(&buf[..n]);
}
#[cfg(feature = "trace")]
pub fn reannounce_stream_header() {
use core::sync::atomic::Ordering;
let cpu_hz = CACHED_CPU_HZ.load(Ordering::Relaxed);
if cpu_hz == 0 {
return;
}
stream_header(cpu_hz, CACHED_MAX_HARTS.load(Ordering::Relaxed));
}
#[cfg(feature = "trace")]
pub fn task_created(task_id: u16, priority: u8, stack_size: u32) {
let mut payload = [0u8; 5];
payload[0] = priority;
payload[1..5].copy_from_slice(&stack_size.to_le_bytes());
let mut buf = [0u8; MAX_FRAME];
let n = encode(
&mut buf,
kind::TASK_CREATED,
core_id(),
task_id as u32,
now_ts(),
&payload,
);
crate::port::board::trace_write(&buf[..n]);
}
#[cfg(feature = "trace")]
pub fn context_switch(prev_task: u16, next_task: u16, reason: SwitchReason) {
let mut payload = [0u8; 9];
payload[0..4].copy_from_slice(&(prev_task as u32).to_le_bytes());
payload[4..8].copy_from_slice(&(next_task as u32).to_le_bytes());
payload[8] = reason as u8;
let mut buf = [0u8; MAX_FRAME];
let n = encode(
&mut buf,
kind::CONTEXT_SWITCH,
core_id(),
next_task as u32,
now_ts(),
&payload,
);
crate::port::board::trace_write(&buf[..n]);
}
#[cfg(feature = "trace")]
pub fn isr(irq: u32, entry: bool) {
let mut payload = [0u8; 4];
payload.copy_from_slice(&irq.to_le_bytes());
let mut buf = [0u8; MAX_FRAME];
let k = if entry { kind::IRQ_ENTER } else { kind::IRQ_EXIT };
let n = encode(&mut buf, k, core_id(), NO_TASK, now_ts(), &payload);
crate::port::board::trace_write(&buf[..n]);
}
#[cfg(feature = "trace")]
pub fn mutex_lock_acquired(task_id: u16, mutex_id: u32) {
let mut payload = [0u8; 4];
payload.copy_from_slice(&mutex_id.to_le_bytes());
let mut buf = [0u8; MAX_FRAME];
let n = encode(
&mut buf,
kind::MUTEX_LOCK_ACQUIRED,
core_id(),
task_id as u32,
now_ts(),
&payload,
);
crate::port::board::trace_write(&buf[..n]);
}
#[cfg(feature = "trace")]
pub fn mutex_unlock(task_id: u16, mutex_id: u32) {
let mut payload = [0u8; 4];
payload.copy_from_slice(&mutex_id.to_le_bytes());
let mut buf = [0u8; MAX_FRAME];
let n = encode(&mut buf, kind::MUTEX_UNLOCK, core_id(), task_id as u32, now_ts(), &payload);
crate::port::board::trace_write(&buf[..n]);
}
#[cfg(feature = "trace")]
pub fn priority_inherit(task_id: u16, mutex_id: u32) {
let mut payload = [0u8; 4];
payload.copy_from_slice(&mutex_id.to_le_bytes());
let mut buf = [0u8; MAX_FRAME];
let n = encode(
&mut buf,
kind::PRIORITY_INHERIT,
core_id(),
task_id as u32,
now_ts(),
&payload,
);
crate::port::board::trace_write(&buf[..n]);
}
#[cfg(feature = "trace")]
pub fn fault(task_id: u16, reason: u8, pc: u32) {
let mut payload = [0u8; 8];
payload[0..4].copy_from_slice(&(reason as u32).to_le_bytes());
payload[4..8].copy_from_slice(&pc.to_le_bytes());
let mut buf = [0u8; MAX_FRAME];
let k = if reason == 4 { kind::STACK_OVERFLOW } else { kind::HARD_FAULT };
let n = encode(&mut buf, k, core_id(), task_id as u32, now_ts(), &payload);
crate::port::board::trace_write(&buf[..n]);
}
#[cfg(feature = "trace")]
pub fn reannounce_all_tasks() {
use crate::preempt::tcb;
use core::sync::atomic::Ordering;
for id in 0..tcb::MAX_PTASKS {
let Some(t) = tcb::get(id) else { continue };
if !t.used.load(Ordering::Acquire) {
continue;
}
let priority = t.base_priority.load(Ordering::Acquire);
let stack_size = t.stack_size.load(Ordering::Acquire) as u32;
task_created(id as u16, priority, stack_size);
}
}