Skip to main content

atmos/kernel/
uart.rs

1use core::fmt;
2
3const UART0_BASE: usize = 0x3F20_1000;
4
5const UART0_DR: *mut u32 = UART0_BASE as *mut u32;
6const UART0_FR: *mut u32 = (UART0_BASE + 0x18) as *mut u32;
7const UART0_IBRD: *mut u32 = (UART0_BASE + 0x24) as *mut u32;
8const UART0_FBRD: *mut u32 = (UART0_BASE + 0x28) as *mut u32;
9const UART0_LCRH: *mut u32 = (UART0_BASE + 0x2C) as *mut u32;
10const UART0_CR: *mut u32 = (UART0_BASE + 0x30) as *mut u32;
11const UART0_IMSC: *mut u32 = (UART0_BASE + 0x38) as *mut u32;
12const UART0_ICR: *mut u32 = (UART0_BASE + 0x44) as *mut u32;
13
14// GPIO レジスタ
15const GPIO_BASE: usize = 0x3F20_0000;
16const GPFSEL1: *mut u32 = (GPIO_BASE + 0x04) as *mut u32;
17const GPPUD: *mut u32 = (GPIO_BASE + 0x94) as *mut u32;
18const GPPUDCLK0: *mut u32 = (GPIO_BASE + 0x98) as *mut u32;
19
20pub struct Uart;
21
22static mut SERIAL_CHAR_COUNT: usize = 0;
23
24impl Uart {
25    /// Mailbox 経由で UART クロック周波数を取得 (ヒープ不要)
26    fn query_uart_clock() -> u32 {
27        // UART0 の Clock ID = 0x000000002
28        // Mailbox Property Tag: Get Clock Rate = 0x00030002
29        #[repr(C, align(16))]
30        struct ClockReq {
31            buf: [u32; 9],
32        }
33        let mut req = ClockReq {
34            buf: [
35                9 * 4,       // Buffer size
36                0,           // Request code
37                0x0003_0002, // Tag: Get Clock Rate
38                8,           // Value buffer size
39                0,           // Request length
40                0x0000_0002, // Clock ID = UART
41                0,           // Response: rate
42                0,           // End tag
43                0,
44            ],
45        };
46        let ptr = req.buf.as_mut_ptr();
47        let slice = unsafe { core::slice::from_raw_parts_mut(ptr, 9) };
48
49        // D-Cache フラッシュ
50        unsafe {
51            let addr = slice.as_ptr() as usize;
52            let cache_line = 64usize;
53            let start = addr & !(cache_line - 1);
54            let end = addr + 9 * 4;
55            let mut cur = start;
56            while cur < end {
57                core::arch::asm!("dc civac, {}", in(reg) cur, options(nostack));
58                cur += cache_line;
59            }
60            core::arch::asm!("dsb sy", options(nostack));
61        }
62
63        // Mailbox registers
64        const MBOX_BASE: usize = 0x3F00_B880;
65        const MBOX_READ: *mut u32 = MBOX_BASE as *mut u32;
66        const MBOX_STATUS: *mut u32 = (MBOX_BASE + 0x18) as *mut u32;
67        const MBOX_WRITE: *mut u32 = (MBOX_BASE + 0x20) as *mut u32;
68
69        let addr_val = req.buf.as_ptr() as u32;
70        let data = (addr_val & !0xF) | 8; // Channel 8 = Property
71
72        unsafe {
73            while (core::ptr::read_volatile(MBOX_STATUS) & 0x8000_0000) != 0 {}
74            core::ptr::write_volatile(MBOX_WRITE, data);
75            loop {
76                while (core::ptr::read_volatile(MBOX_STATUS) & 0x4000_0000) != 0 {}
77                let res = core::ptr::read_volatile(MBOX_READ);
78                if res == data {
79                    break;
80                }
81            }
82            // D-Cache invalidate to read back GPU response
83            let addr = slice.as_ptr() as usize;
84            let cache_line = 64usize;
85            let start = addr & !(cache_line - 1);
86            let end = addr + 9 * 4;
87            let mut cur = start;
88            while cur < end {
89                core::arch::asm!("dc civac, {}", in(reg) cur, options(nostack));
90                cur += cache_line;
91            }
92            core::arch::asm!("dsb sy", options(nostack));
93        }
94
95        if req.buf[1] == 0x8000_0000 && req.buf[6] != 0 {
96            req.buf[6]
97        } else {
98            48_000_000 // フォールバック: 実機のデフォルト 48MHz
99        }
100    }
101
102    pub fn init() {
103        unsafe {
104            // 1. UARTを無効化
105            core::ptr::write_volatile(UART0_CR, 0);
106
107            // 2. GPIO 14/15 を ALT0 (UART0 TXD/RXD) に設定
108            let mut gpfsel1 = core::ptr::read_volatile(GPFSEL1);
109            // GPIO 14: FSEL14 = bits [14:12], ALT0 = 0b100
110            gpfsel1 &= !(0b111 << 12);
111            gpfsel1 |= 0b100 << 12;
112            // GPIO 15: FSEL15 = bits [17:15], ALT0 = 0b100
113            gpfsel1 &= !(0b111 << 15);
114            gpfsel1 |= 0b100 << 15;
115            core::ptr::write_volatile(GPFSEL1, gpfsel1);
116
117            // 3. GPIO 14/15 のプルアップ/ダウンをクリア
118            core::ptr::write_volatile(GPPUD, 0);
119            for _ in 0..150 {
120                core::arch::asm!("nop");
121            }
122            core::ptr::write_volatile(GPPUDCLK0, (1 << 14) | (1 << 15));
123            for _ in 0..150 {
124                core::arch::asm!("nop");
125            }
126            core::ptr::write_volatile(GPPUDCLK0, 0);
127
128            // 4. 全割り込みクリア
129            core::ptr::write_volatile(UART0_ICR, 0x7FF);
130
131            // 5. すべての割り込みをマスク
132            core::ptr::write_volatile(UART0_IMSC, 0);
133
134            // 6. Mailbox でUARTクロック周波数を取得し、動的にボーレートを計算
135            // これにより実機 (48MHz) でもQEMU (~3MHz) でも正しく 115200 baud で動作する
136            let uart_clk = Self::query_uart_clock();
137            let baud = 115200u32;
138            let divisor_x64 = ((uart_clk as u64) * 4) / (baud as u64); // = 64 * (clk / (16 * baud))
139            let ibrd = (divisor_x64 / 64) as u32;
140            let fbrd = (divisor_x64 % 64) as u32;
141            core::ptr::write_volatile(UART0_IBRD, ibrd);
142            core::ptr::write_volatile(UART0_FBRD, fbrd);
143
144            // 7. 8bit, FIFO有効
145            core::ptr::write_volatile(UART0_LCRH, 0x70);
146
147            // 8. UART, TX, RX を有効化
148            core::ptr::write_volatile(UART0_CR, 0x301);
149        }
150    }
151
152    pub fn send(c: u8) {
153        unsafe {
154            // 送信バッファが空くまで待つ (FR の TXFF (Transmit FIFO Full) ビットが 0 になるまで)
155            while (core::ptr::read_volatile(UART0_FR) & 0x20) != 0 {}
156            core::ptr::write_volatile(UART0_DR, c as u32);
157        }
158    }
159
160    /// 受信データがあるか確認します (FRレジスタの RXFE (Receive FIFO Empty) ビットが0ならデータあり)
161    pub fn has_char() -> bool {
162        unsafe { (core::ptr::read_volatile(UART0_FR) & 0x10) == 0 }
163    }
164
165    /// 1バイト受信します (データが来るまで待機します)
166    pub fn recv() -> u8 {
167        unsafe {
168            while !Self::has_char() {}
169            (core::ptr::read_volatile(UART0_DR) & 0xFF) as u8
170        }
171    }
172
173    pub fn send_string(s: &str) {
174        unsafe {
175            let mut prev = 0u8;
176            for b in s.bytes() {
177                if b == b'\n' {
178                    if prev != b'\r' {
179                        Self::send(b'\r');
180                    }
181                    Self::send(b);
182                    SERIAL_CHAR_COUNT = 0;
183                    prev = b;
184                } else {
185                    if !crate::AUTO_TEST_BROWSER && SERIAL_CHAR_COUNT >= 55 {
186                        Self::send(b'\r');
187                        Self::send(b'\n');
188                        SERIAL_CHAR_COUNT = 0;
189                    }
190                    Self::send(b);
191                    if !crate::AUTO_TEST_BROWSER {
192                        SERIAL_CHAR_COUNT += 1;
193                    }
194                    prev = b;
195                }
196            }
197        }
198    }
199}
200
201impl fmt::Write for Uart {
202    fn write_str(&mut self, s: &str) -> fmt::Result {
203        Self::send_string(s);
204        Ok(())
205    }
206}
207
208// 簡単なマクロを実装
209#[macro_export]
210macro_rules! print {
211    ($($arg:tt)*) => {
212        // 【2026-08-05】ログ行と直列化する。以前はここが素の書き込みで、
213        // ログ行の途中へ割り込んで壊していた。
214        $crate::kernel::uart::emit_raw(core::format_args!($($arg)*));
215    };
216}
217
218#[macro_export]
219macro_rules! println {
220    () => ($crate::print!("\n"));
221    // 【2026-08-05】以前は本文と改行で `print!` を 2 回呼んでいた。
222    // 1 回ずつはロックされていても**その隙間**に他スレッドが割り込めるため、
223    // 改行の直前で行が割られていた(実測でこの形の混線が 1 件残っていた)。
224    // `format_args!` の時点で改行まで含めて 1 回の出力にする。
225    ($($arg:tt)*) => ({
226        $crate::kernel::uart::emit_raw(core::format_args!("{}\n", core::format_args!($($arg)*)));
227    })
228}
229
230use core::sync::atomic::{AtomicBool, Ordering};
231
232static IS_SILENT: AtomicBool = AtomicBool::new(false);
233
234pub fn set_silent(silent: bool) {
235    IS_SILENT.store(silent, Ordering::Relaxed);
236}
237
238pub fn is_silent() -> bool {
239    IS_SILENT.load(Ordering::Relaxed)
240}
241
242/// ログ 1 行の出力を直列化するロック。
243///
244/// 【2026-08-05】以前は `write_fmt` を 3 回(レベル・本文・改行)
245/// 素で呼んでいた。UART へは 1 文字ずつ書くため、途中でスレッドが
246/// 切り替わると**行が混ざる**。リソース取得を 4 並列にしてから頻発し、
247/// 実際のログにこうした壊れ方が現れていた:
248///
249/// ```text
250/// [NET][DNSSEND]CgQ hoBnstw7HFyzSD-As=fonts.gstatic.com attempt=1
251/// ```
252///
253/// 診断の土台が壊れると、他のすべての調査が信用できなくなる。
254static LOG_LOCK: spin::Mutex<()> = spin::Mutex::new(());
255
256/// ログ 1 行を組み立てて、まとめて出力する。
257///
258/// # デッドロックを作らないための決め事
259///
260/// ロックが取れないまま**有限回**спинしたら、諦めてロック無しで出す。
261/// 行が混ざるのは困るが、ログが原因で系全体が止まる方がはるかに悪い。
262/// 例外ハンドラがロックを握ったまま死んだ場合でも、これで先へ進める。
263/// ログ用ロックを有限回だけ待つ。取れなければ `None`。
264///
265/// 取れなくても**必ず出力はする**(呼び出し側で `None` のまま進む)。
266/// 行が混ざるのは困るが、ログが原因で系全体が止まる方がはるかに悪い。
267/// 例外ハンドラがロックを握ったまま死んだ場合でも、これで先へ進める。
268/// # 上限は「時間」で決める
269///
270/// 【2026-08-05】最初は回数(10 万回)で切ったが**短すぎた**。
271/// 115200 baud では 1 文字 ≈ 87µs、100 文字の行で ≈ 9ms かかる。
272/// 一方 10 万回のスピンは 1ms にも満たないので、
273/// 先行する行を書き終える前に諦めて混線が残っていた
274/// (実測: 399 行中 2 行)。
275///
276/// tick 基準にすれば CPU 速度に依らず「行 1 本ぶんは待つ」が保証できる。
277/// tick が進まない状況(呼び出し元が割り込みを止めている等)でも
278/// 止まらないよう、回数の上限も併せて置く。
279fn try_lock_log() -> Option<spin::MutexGuard<'static, ()>> {
280    let start = crate::kernel::timer::get_ticks();
281    let mut spins: u32 = 0;
282    loop {
283        if let Some(g) = LOG_LOCK.try_lock() {
284            return Some(g);
285        }
286        core::hint::spin_loop();
287        spins = spins.wrapping_add(1);
288        // 20 tick(≈200ms)待てば、どんな長さの行でも書き終わっている。
289        if crate::kernel::timer::get_ticks().wrapping_sub(start) > 20 {
290            return None;
291        }
292        // tick が進まない場合の保険。
293        if spins > 50_000_000 {
294            return None;
295        }
296    }
297}
298
299/// `print!` の出力をログ行と直列化する。
300///
301/// 【2026-08-05】`log!` 側を直列化しただけでは足りなかった。
302/// `print!` は同じ UART へ**ロックを通さずに**書くため、
303/// ログ行の途中に割り込んで壊していた(実測で `[PERF] style_t` の
304/// ように途中で切れた行が残っていた)。
305///
306/// `print!` は端末表示にも使い、長さも改行の有無も自由なので、
307/// `emit_line` のような固定長バッファは通さずロックだけ掛ける。
308pub fn emit_raw(args: core::fmt::Arguments) {
309    use core::fmt::Write;
310    let guard = try_lock_log();
311    let mut uart = Uart;
312    let _ = uart.write_fmt(args);
313    #[allow(unused_unsafe)]
314    unsafe {
315        if crate::kernel::draw::SPLASH_ACTIVE {
316            let mut writer = crate::kernel::draw::SplashWriter;
317            let _ = writer.write_fmt(args);
318        }
319    }
320    drop(guard);
321}
322
323pub fn emit_line(level_str: &str, args: core::fmt::Arguments) {
324    use core::fmt::Write;
325    let mut line = crate::kernel::uart_line_buf::LineBuf::new();
326    let _ = line.write_str(level_str);
327    let _ = line.write_str(" ");
328    let _ = line.write_fmt(args);
329    if line.was_truncated() {
330        // 切り詰めは UTF-8 の途中で起きうる。文字境界まで戻してから
331        // 印を付ける(不正なバイト列を UART へ流さないため)。
332        line.trim_to_char_boundary();
333        let _ = line.write_str("...");
334    }
335    let _ = line.write_str("\n");
336
337    let guard = try_lock_log();
338
339    // 1 回で書き切る。ここを分けると混線の余地が戻る。
340    let text = core::str::from_utf8(line.as_bytes()).unwrap_or("[log: invalid utf-8]\n");
341    let mut uart = Uart;
342    let _ = uart.write_str(text);
343    #[allow(unused_unsafe)]
344    unsafe {
345        if crate::kernel::draw::SPLASH_ACTIVE {
346            let mut writer = crate::kernel::draw::SplashWriter;
347            let _ = writer.write_str(text);
348        }
349    }
350    drop(guard);
351}
352
353#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
354pub enum LogLevel {
355    Error = 0,
356    Warn = 1,
357    Info = 2,
358    Debug = 3,
359}
360
361/// システム全体のログ出力レベル(マスター音量ノブ)。
362pub const CURRENT_LOG_LEVEL: LogLevel = LogLevel::Warn;
363
364#[macro_export]
365macro_rules! log {
366    ($level:expr, $($arg:tt)*) => {
367        if !$crate::kernel::uart::is_silent() {
368            if $level <= $crate::kernel::uart::CURRENT_LOG_LEVEL {
369                let level_str = match $level {
370                    $crate::kernel::uart::LogLevel::Error => "[ERROR]",
371                    $crate::kernel::uart::LogLevel::Warn  => "[WARN ]",
372                    $crate::kernel::uart::LogLevel::Info  => "[INFO ]",
373                    $crate::kernel::uart::LogLevel::Debug => "[DEBUG]",
374                };
375                // 【2026-08-05】以前はここで `write_fmt` を 3 回
376                // (レベル・本文・改行)素で呼んでいた。UART へは 1 文字ずつ
377                // 書くので、途中でスレッドが切り替わると**行が混ざる**。
378                // 4 並列でリソースを取るようにしてから頻発していた。
379                // 組み立てと出力は `emit_line` に集約する。
380                $crate::kernel::uart::emit_line(level_str, core::format_args!($($arg)*));
381            }
382        }
383    };
384}
385
386#[macro_export]
387macro_rules! error {
388    ($($arg:tt)*) => ($crate::log!($crate::kernel::uart::LogLevel::Error, $($arg)*));
389}
390
391#[macro_export]
392macro_rules! warn {
393    ($($arg:tt)*) => ($crate::log!($crate::kernel::uart::LogLevel::Warn, $($arg)*));
394}
395
396#[macro_export]
397macro_rules! info {
398    ($($arg:tt)*) => ($crate::log!($crate::kernel::uart::LogLevel::Info, $($arg)*));
399}
400
401#[macro_export]
402macro_rules! debug {
403    ($($arg:tt)*) => ($crate::log!($crate::kernel::uart::LogLevel::Debug, $($arg)*));
404}