1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
/* debug.rs
*
* Developed by Tim Walls <tim.walls@snowgoons.com>
* Copyright (c) All Rights Reserved, Tim Walls
*/
//! Helpful debugging utilities.
//!
//! These are used to output to the serial port that was configured using
//! one of the `panicout` feature flags.
//!
//! It is your responsibility to initialise that port with a
//! suitable baud rate/serial protocol as early as possible in your program.
//!
//! | For Processor | Available panic output Features (pick 1) |
//! | -------------- | ---------------------------------------- |
//! | `atmega4809` | `panicout_usart0`, `panicout_usart1`, `panicout_usart2`, `panicout_usart3` |
//! | `atmega328p` | `panicout_usart0` |
//!
//! # Synchronous/Blocking
//! These functions all use synchronous transmit functions, and will block
//! the processor entirely (disabling interrupts) during execution. This
//! makes them resilient to any failures (e.g. deadlocks or similar)
//! in the operating system, helpful for debugging - but means they are
//! absolutely NOT to be used for general I/O.
// Imports ===================================================================
use avr_oxide::concurrency::util::ThreadId;
use avr_oxide::deviceconsts::oxide;
use avr_oxide::concurrency::stack;
#[cfg(feature="panicout")]
use avr_oxide::hal::generic::serial::SerialNoInterruptTx;
// Declarations ==============================================================
// Code ======================================================================
/// Print a string to the serial port configured with a `panic_stdout`
/// feature.
/// If no such feature is enabled, this call does nothing.
pub fn print(string: &str){
#[cfg(all(feature="panicout",target_arch="avr"))]
if avr_oxide::panic_stdout!().can_write() {
avr_oxide::panic_stdout!().blocking_write_slice(string.as_bytes());
}
}
/// Print a u16 to the serial port configured with a `panic_stdout`
/// feature, as a decimal.
///
/// If no such feature is enabled, this call does nothing.
pub fn print_u16(val: u16){
if val > 9 {
print_u16(val/10);
}
#[cfg(all(feature="panicout",target_arch="avr"))]
if avr_oxide::panic_stdout!().can_write() {
avr_oxide::panic_stdout!().blocking_write_u8(((val % 10) + 0x30) as u8);
}
}
/// Print a u32 to the serial port configured with a `panic_stdout`
/// feature, as a decimal.
///
/// If no such feature is enabled, this call does nothing.
pub fn print_u32(val: u32){
if val > 9 {
print_u32(val/10);
}
#[cfg(all(feature="panicout",target_arch="avr"))]
if avr_oxide::panic_stdout!().can_write() {
avr_oxide::panic_stdout!().blocking_write_u8(((val % 10) + 0x30) as u8);
}
}
/// Print a summary of all currently loaded threads and their state to
/// the serial port configured by the `panic_stdout` feature.
///
/// A description of how to interpret the provided data can be found
/// on the [AVRoxide Site](https://avroxi.de/post/thread-dump-format/)
pub fn print_thread_state() {
use avr_oxide::concurrency::scheduler;
#[cfg(all(feature="panicout",target_arch="avr"))]
avr_oxide::concurrency::interrupt::isolated(|isotoken|{
if avr_oxide::panic_stdout!().can_write() {
unsafe {
print("\r\nKRNL");
if stack::kernel::is_stack_guard_valid() {
print("\r\n OK");
} else {
print("\r\n BAD");
}
print("\r\n S: ");
print_u16(stack::kernel::get_size() as u16);
print(" @ ");
print_u16(stack::kernel::get_stack_top() as u16);
print("\r\n F: ");
print_u16(stack::kernel::get_stack_free() as u16);
for thread_id in ThreadId::MIN..oxide::MAX_THREADS {
match scheduler::try_get_thread_by_id(thread_id){
None => {},
Some(thread) => {
print("\r\n\nT");
print_u16(thread_id as u16);
print(":\r\n ");
print(thread.state.to_debug_str());
print("\r\n RC: ");
print_u16(thread.returncode as u16);
print("\r\n Wt: ");
thread.waiting_threads.do_each(isotoken, |_isotoken,waiting_id|{
print_u16(waiting_id as u16);
print(" ");
true
});
print("\r\n ST:");
match &thread.stack {
None => {
print("\r\n DISP");
},
Some(stack) => {
if stack.is_stack_guard_valid() {
print("\r\n OK");
} else {
print("\r\n BAD");
}
print("\r\n S: ");
print_u16(stack.get_size() as u16);
print(" @ ");
print_u16(stack.get_stack_top() as u16);
print("\r\n F: ");
print_u16(stack.get_stack_free() as u16);
}
}
}
}
}
print("\r\n\n");
}
}
});
}
// Tests =====================================================================