arm_dcc/lib.rs
1//! [Debug Communication Channel][dcc] (DCC) API
2//!
3//! [dcc]: https://developer.arm.com/products/software-development-tools/compilers/arm-compiler-5/docs/dui0471/latest/debug-communications-channel
4//!
5//! # Example
6//!
7//! ## Device side
8//!
9//! ``` no_run
10//! use arm_dcc::dprintln;
11//!
12//! fn main() {
13//! dprintln!("Hello, world!");
14//! }
15//! ```
16//!
17//! ## Host side
18//!
19//! ``` text
20//! $ # XSDB = Xilinx System Debugger
21//! $ xsdb
22//!
23//! xsdb% # connect
24//! xsdb% conn
25//!
26//! xsdb% # select a Cortex-R core
27//! xsdb% targets -set 0
28//!
29//! xsdb% # hold the processor in reset state
30//! xsdb% rst -processor
31//!
32//! xsdb% # load program
33//! xsdb% dow hello.elf
34//!
35//! xsdb% # open a file
36//! xsdb% set f [open dcc.log w]
37//!
38//! xsdb% # redirect DCC output to file handle `f`
39//! xsdb% readjtaguart -start -handle $f
40//!
41//! xsdb% # start program execution
42//! xsdb% con
43//! ```
44//!
45//! ``` text
46//! $ # on another terminal
47//! $ tail -f dcc.log
48//! Hello, world!
49//! ```
50//!
51//! # Supported Rust version
52//!
53//! - Rust >=1.59
54//!
55//! # Optional features
56//!
57//! ## `nop`
58//!
59//! Turns `dcc::write` into a "no-operation" (not the instruction). This is useful when the DCC is
60//! disabled as `dcc::write` blocks forever in that case.
61
62#![deny(missing_docs)]
63#![no_std]
64
65use core::fmt;
66
67/// Macro for printing to the DCC
68#[macro_export]
69macro_rules! dprint {
70 ($s:expr) => {
71 $crate::write_str($s)
72 };
73 ($($tt:tt)*) => {
74 $crate::write_fmt(format_args!($($tt)*))
75 };
76}
77
78/// Macro for printing to the DCC, with a newline.
79#[macro_export]
80macro_rules! dprintln {
81 () => {
82 $crate::write_str("\n")
83 };
84 ($s:expr) => {
85 $crate::write_str(concat!($s, "\n"))
86 };
87 ($s:expr, $($tt:tt)*) => {
88 $crate::write_fmt(format_args!(concat!($s, "\n"), $($tt)*))
89 };
90}
91
92/// Proxy struct that implements the `fmt::Write`
93///
94/// The main use case for this is using the `write!` macro
95pub struct Writer;
96
97impl fmt::Write for Writer {
98 fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
99 write_str(s);
100 Ok(())
101 }
102}
103
104/// Writes a single word to the DCC
105///
106/// **NOTE:** This operation is blocking
107#[allow(unused_variables)]
108#[inline(always)]
109pub fn write(word: u32) {
110 match () {
111 #[cfg(not(target_arch = "arm"))]
112 () => unimplemented!(),
113 #[cfg(all(target_arch = "arm", feature = "nop"))]
114 () => {}
115 #[cfg(all(target_arch = "arm", not(feature = "nop")))]
116 () => {
117 const W: u32 = 1 << 29;
118
119 unsafe {
120 let mut r: u32;
121 loop {
122 // busy wait until we can send data
123 core::arch::asm!("MRC p14, 0, {}, c0, c1, 0", out(reg) r);
124 if r & W == 0 {
125 break;
126 }
127 }
128 core::arch::asm!("MCR p14, 0, {}, c0, c5, 0", in(reg) word);
129 }
130 }
131 }
132}
133
134/// Writes the bytes to the DCC
135///
136/// NOTE: each byte will be word-extended before being `write`-n to the DCC
137pub fn write_all(bytes: &[u8]) {
138 bytes.iter().for_each(|byte| write(u32::from(*byte)))
139}
140
141#[doc(hidden)]
142pub fn write_fmt(args: fmt::Arguments) {
143 use core::fmt::Write;
144
145 Writer.write_fmt(args).ok();
146}
147
148/// Writes the string to the DCC
149pub fn write_str(string: &str) {
150 write_all(string.as_bytes())
151}
152
153#[cfg(target_arch = "arm")]
154core::arch::global_asm!(
155 r#"
156 // Routine for putting data in the DCC register
157 .section .text.__dcc_write
158 .global __dcc_write
159__dcc_write:
1601: mrc p14, 0, r1, c0, c1, 0
161 tst r1, #536870912 /* 0x20000000 */
162 bne 1b
163 mcr p14, 0, r0, c0, c5, 0
164 bx lr
165 "#
166);