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
//! NS16550A UART 驱动
//!
//! QEMU virt 平台将 NS16550A 兼容 UART 映射到物理地址 0x1000_0000。
//! 本驱动在 S-mode 下通过 MMIO 直接访问硬件寄存器,无需 SBI 调用。
//!
//! ## NS16550A 寄存器(以字节方式访问,基址 = 0x1000_0000)
//!
//! | 偏移 | DLAB=0 读 | DLAB=0 写 | DLAB=1 |
//! |------|-----------|-----------|------------|
//! | 0 | RBR(接收)| THR(发送)| DLL(波特率低字节)|
//! | 1 | IER(中断)| IER | DLM(波特率高字节)|
//! | 2 | IIR(中断状态)| FCR(FIFO)| |
//! | 3 | LCR(线路控制)| LCR | |
//! | 4 | MCR(调制解调器)| MCR | |
//! | 5 | LSR(线路状态)| — | |
//!
//! ## 初始化步骤
//!
//! 1. 禁用中断(IER = 0x00)
//! 2. 进入波特率设置模式(LCR.DLAB = 1)
//! 3. 设置波特率除数(QEMU virt 时钟 3686400 Hz,115200 波特,除数 = 2)
//! 4. 设置帧格式(LCR = 8N1,DLAB = 0)
//! 5. 使能并复位 FIFO(FCR = 0xC7)
//! 6. 使能 DTR/RTS(MCR = 0x0B)
/// UART 基址(QEMU virt 平台)
const UART_BASE: usize = 0x1000_0000;
// ----- 寄存器偏移量 -----
const RBR: usize = UART_BASE + 0; // Receive Buffer Register (DLAB=0, read)
const THR: usize = UART_BASE + 0; // Transmit Holding Register (DLAB=0, write)
const DLL: usize = UART_BASE + 0; // Divisor Latch Low (DLAB=1)
const IER: usize = UART_BASE + 1; // Interrupt Enable Register (DLAB=0)
const DLM: usize = UART_BASE + 1; // Divisor Latch High (DLAB=1)
const FCR: usize = UART_BASE + 2; // FIFO Control Register (write)
const LCR: usize = UART_BASE + 3; // Line Control Register
const MCR: usize = UART_BASE + 4; // Modem Control Register
const LSR: usize = UART_BASE + 5; // Line Status Register
// ----- LSR 位定义 -----
/// 发送保持寄存器空(可写入下一字节)
const LSR_THRE: u8 = 1 << 5;
// ----- 辅助函数 -----
unsafe
unsafe
/// 初始化 NS16550A UART。
///
/// 配置为:115200 波特,8 位数据,无校验,1 位停止位(8N1),
/// 使能 16 字节发送/接收 FIFO。
///
/// # Safety
/// 必须在访问 UART 之前调用一次,且调用者须保证 UART_BASE 已映射可访问。
/// 向 UART 发送一个字节。
///
/// 忙等待直到发送保持寄存器为空(LSR.THRE = 1),再写入 THR。
/// 向 UART 发送一个字节串。