├── .gitignore ├── Cargo.toml ├── README.md └── src ├── print_rtt.rs └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blash-target" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | riscv = "0.7.0" 10 | bl602-hal = { git = "https://github.com/sipeed/bl602-hal", branch="main" } 11 | 12 | [features] 13 | panic_backtrace = [] 14 | exception_backtrace = [] 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # blash-target 2 | 3 | This provides RTT print and optionally a panic handler and exception handler to be used with https://github.com/bjoernQ/blash 4 | 5 | Also see https://github.com/bjoernQ/bl602-rtt-example 6 | 7 | The panic and exception handler are both feature gated: 8 | - panic_backtrace 9 | - exception_backtrace 10 | 11 | If you don't get full backtraces you should compile the target application like `cargo build -Z build-std=core --target riscv32imc-unknown-none-elf` to make sure there is unwind info for all the code. 12 | -------------------------------------------------------------------------------- /src/print_rtt.rs: -------------------------------------------------------------------------------- 1 | /// This is an oversimplified RTT implementation 2 | /// supporting only one UP channel. 3 | /// It doesn't care about overflowing the output. 4 | /// (It just doesn't check the read_offset). 5 | /// But for normal logging purposes that should be good enough for now. 6 | use core::{fmt::Write, ptr}; 7 | 8 | static mut BUFFER: [u8; 1024] = [0u8; 1024]; 9 | 10 | #[repr(C)] 11 | struct Buffer { 12 | name: *const u8, 13 | buf_start: *mut u8, 14 | size_of_buffer: u32, 15 | /// Position of next data to be written 16 | write_offset: u32, 17 | /// Position of next data to be read by host. 18 | read_offset: u32, 19 | /// In the segger library these flags control blocking 20 | /// or non-blocking behavior. This implementation 21 | /// is non-blocking. 22 | flags: u32, 23 | } 24 | 25 | #[repr(C)] 26 | pub struct ControlBlock { 27 | /// Initialized to "SEGGER RTT" 28 | id: [u8; 16], 29 | /// Initialized to NUM_UP 30 | up_buffers: i32, 31 | /// Initialized to NUM_DOWN 32 | down_buffers: i32, 33 | /// Note that RTT allows for this to be an array of 34 | /// "up" buffers of size up_buffers, but here we only have one. 35 | up: Buffer, 36 | } 37 | 38 | static CHANNEL_NAME: &[u8] = b"Terminal\0"; 39 | 40 | #[no_mangle] 41 | pub static mut _SEGGER_RTT: ControlBlock = ControlBlock { 42 | id: [0u8; 16], 43 | up_buffers: 1, 44 | down_buffers: 0, 45 | up: Buffer { 46 | name: &CHANNEL_NAME as *const _ as *const u8, 47 | buf_start: unsafe { &mut BUFFER as *mut u8 }, 48 | size_of_buffer: 1024, 49 | write_offset: 0, 50 | read_offset: 0, 51 | flags: 0, 52 | }, 53 | }; 54 | 55 | pub struct Output {} 56 | 57 | impl Output { 58 | pub fn new() -> Output { 59 | unsafe { 60 | ptr::copy_nonoverlapping(b"SEGG_" as *const u8, _SEGGER_RTT.id.as_mut_ptr(), 5); 61 | 62 | ptr::copy_nonoverlapping( 63 | b"ER RTT\0\0\0\0\0\0" as *const u8, 64 | _SEGGER_RTT.id.as_mut_ptr().offset(4), 65 | 12, 66 | ); 67 | } 68 | 69 | Output {} 70 | } 71 | 72 | fn write_str_internal(&mut self, s: &str) -> usize { 73 | let len = s.len(); 74 | 75 | unsafe { 76 | let buf_len = BUFFER.len() as u32; 77 | let write_offset = _SEGGER_RTT.up.write_offset as isize; 78 | let count = usize::min(BUFFER.len() - write_offset as usize - 1, len); 79 | 80 | core::intrinsics::copy_nonoverlapping( 81 | s.as_ptr() as *const u8, 82 | BUFFER.as_mut_ptr().offset(write_offset), 83 | count, 84 | ); 85 | 86 | let mut new_write_off = write_offset as u32 + count as u32; 87 | if new_write_off >= buf_len - 1 { 88 | new_write_off = 0; 89 | } 90 | 91 | _SEGGER_RTT.up.write_offset = new_write_off; 92 | 93 | count 94 | } 95 | } 96 | } 97 | 98 | impl Write for Output { 99 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 100 | let mut offset = 0; 101 | let mut to_write = s.len(); 102 | loop { 103 | let written = self.write_str_internal(&s[offset..]); 104 | 105 | if written == to_write { 106 | break; 107 | } 108 | 109 | offset = written; 110 | to_write -= written; 111 | } 112 | 113 | Ok(()) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | use core::mem::MaybeUninit; 4 | mod print_rtt; 5 | 6 | pub fn _construct_output() -> print_rtt::Output { 7 | print_rtt::Output::new() 8 | } 9 | 10 | #[repr(C)] 11 | struct TriggerBacktrace { 12 | trigger: u8, 13 | mepc: u32, 14 | sp: u32, 15 | } 16 | 17 | #[used] 18 | #[no_mangle] 19 | static mut _BLASH_BACKTRACE_TRIGGER: TriggerBacktrace = TriggerBacktrace { 20 | trigger: 0, 21 | mepc: 0, 22 | sp: 0, 23 | }; 24 | 25 | pub static mut OUT: MaybeUninit = MaybeUninit::uninit(); 26 | 27 | pub fn out() -> &'static mut dyn core::fmt::Write { 28 | unsafe { &mut *(OUT.as_mut_ptr()) } 29 | } 30 | 31 | #[macro_export] 32 | macro_rules! println { 33 | ($($arg:tt)*) => { 34 | #[allow(unused_unsafe)] 35 | riscv::interrupt::free(|_|{ 36 | let writer = $crate::out(); 37 | writeln!(writer, $($arg)*).ok(); 38 | }); 39 | }; 40 | } 41 | 42 | #[macro_export] 43 | macro_rules! print { 44 | ($($arg:tt)*) => { 45 | #[allow(unused_unsafe)] 46 | riscv::interrupt::free(|_|{ 47 | let writer = $crate::out(); 48 | write!(writer, $($arg)*).ok(); 49 | }); 50 | }; 51 | } 52 | 53 | #[macro_export] 54 | macro_rules! init_print { 55 | () => { 56 | #[allow(unused_unsafe)] 57 | unsafe { 58 | let mut output = $crate::_construct_output(); 59 | unsafe { 60 | *($crate::OUT.as_mut_ptr()) = output; 61 | } 62 | } 63 | }; 64 | } 65 | 66 | #[macro_export] 67 | macro_rules! dbg { 68 | // NOTE: We cannot use `concat!` to make a static string as a format argument 69 | // of `println!` because `file!` could contain a `{` or 70 | // `$val` expression could be a block (`{ .. }`), in which case the `println!` 71 | // will be malformed. 72 | () => { 73 | $crate::println!("[{}:{}]", core::file!(), core::line!()) 74 | }; 75 | ($val:expr $(,)?) => { 76 | // Use of `match` here is intentional because it affects the lifetimes 77 | // of temporaries - https://stackoverflow.com/a/48732525/1063961 78 | match $val { 79 | tmp => { 80 | $crate::println!("[{}:{}] {} = {:#?}", 81 | core::file!(), core::line!(), core::stringify!($val), &tmp); 82 | tmp 83 | } 84 | } 85 | }; 86 | ($($val:expr),+ $(,)?) => { 87 | ($($crate::dbg!($val)),+,) 88 | }; 89 | } 90 | 91 | #[cfg(not(test))] 92 | #[cfg(feature = "panic_backtrace")] 93 | #[panic_handler] 94 | fn panic_handler(info: &core::panic::PanicInfo) -> ! { 95 | println!("PANIC! {:?}", info); 96 | 97 | for _ in 0..50000 {} 98 | 99 | unsafe { 100 | _BLASH_BACKTRACE_TRIGGER.trigger = 1; 101 | } 102 | loop {} 103 | } 104 | 105 | #[cfg(feature = "exception_backtrace")] 106 | #[export_name = "ExceptionHandler"] 107 | fn custom_exception_handler(_trap_frame: &bl602_hal::interrupts::TrapFrame) -> ! { 108 | let mepc = riscv::register::mepc::read(); 109 | let code = riscv::register::mcause::read().code() & 0xff; 110 | let meaning = match code { 111 | 0 => "Instruction address misaligned", 112 | 1 => "Instruction access fault", 113 | 2 => "Illegal instruction", 114 | 3 => "Breakpoint", 115 | 4 => "Load address misaligned", 116 | 5 => "Load access fault", 117 | 6 => "Store/AMO address misaligned", 118 | 7 => "Store/AMO access fault", 119 | 8 => "Environment call from U-mode", 120 | 9 => "Environment call from S-mode", 121 | 10 => "Reserved", 122 | 11 => "Environment call from M-mode", 123 | 12 => "Instruction page fault", 124 | 13 => "Load page fault", 125 | 14 => "Reserved", 126 | 15 => "Store/AMO page fault", 127 | _ => "Unknown", 128 | }; 129 | println!("exception code {} ({}) at {:x}", code, meaning, mepc); 130 | 131 | for _ in 0..50000 {} 132 | unsafe { 133 | _BLASH_BACKTRACE_TRIGGER.mepc = riscv::register::mepc::read() as u32; 134 | _BLASH_BACKTRACE_TRIGGER.sp = _trap_frame.sp as u32; 135 | _BLASH_BACKTRACE_TRIGGER.trigger = 1; 136 | } 137 | loop {} 138 | } 139 | --------------------------------------------------------------------------------