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 =====================================================================