Giao diện
The #![no_std] Universe Bare Metal
Khi Standard Library không tồn tại
1. What Happens When You Disable std?
Standard Library Architecture
RUST LIBRARY HIERARCHY
┌─────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ std (Standard) │ │
│ │ ┌───────────┬───────────┬───────────┬───────────────┐ │ │
│ │ │ I/O │ Threading │ Networking│ OS APIs │ │ │
│ │ │ println! │ threads │ TcpStream│ env vars │ │ │
│ │ └───────────┴───────────┴───────────┴───────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↑ Requires OS │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ alloc (Heap) │ │
│ │ ┌───────────┬───────────┬───────────┬───────────────┐ │ │
│ │ │ Vec │ String │ Box │ Rc │ │ │
│ │ │ vec![] │ "hello" │ Box::new │ Rc::new │ │ │
│ │ └───────────┴───────────┴───────────┴───────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↑ Requires Allocator │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ core (Always) │ │
│ │ ┌───────────┬───────────┬───────────┬───────────────┐ │ │
│ │ │ Option │ Result │ Iterator │ Slices │ │ │
│ │ │ Some(x) │ Ok(x) │ .map() │ &[T] │ │ │
│ │ └───────────┴───────────┴───────────┴───────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ↑ No dependencies │
│ │
└─────────────────────────────────────────────────────────────────┘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
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
Khai báo #![no_std]
rust
// src/main.rs hoặc src/lib.rs
#![no_std] // Không sử dụng standard library
#![no_main] // Không có main() function thông thường
// Bạn vẫn có core library
use core::panic::PanicInfo;
// Entry point phải được định nghĩa riêng
#[no_mangle]
pub extern "C" fn _start() -> ! {
// Kernel hoặc firmware entry point
loop {}
}
// Panic handler bắt buộc phải có
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Những gì MẤT khi dùng no_std
| Mất | Vì sao | Thay thế |
|---|---|---|
println! | Cần stdout (OS) | UART, RTT, custom |
Vec, String | Cần heap allocator | Cung cấp allocator |
std::thread | Cần OS scheduler | Bare metal scheduling |
std::fs | Cần filesystem | Custom FS hoặc không có |
std::net | Cần network stack | smoltcp, custom |
Những gì GIỮ từ core
rust
#![no_std]
// Tất cả những thứ này vẫn hoạt động
use core::option::Option; // Some, None
use core::result::Result; // Ok, Err
use core::slice; // &[T]
use core::str; // &str
use core::iter::Iterator; // .map(), .filter()
use core::mem; // size_of, transmute
use core::ptr; // read_volatile, write_volatile
use core::sync::atomic::AtomicBool; // Atomic operations1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
2. Custom Heap Allocator
Tại sao cần Custom Allocator?
MEMORY REGIONS IN EMBEDDED
┌─────────────────────────────────────────────────────────────────┐
│ │
│ Flash (Read-only) │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ .text (code) │ .rodata (constants) │ .data init │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ RAM │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ .data │ .bss │ HEAP │ STACK │ │
│ │ (globals) │ (zeroed) │ ↓↓↓ │ ↑↑↑ │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │ │
│ └─────────────┘ │
│ Managed by Allocator │
│ │
└─────────────────────────────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Implementing với linked_list_allocator
rust
#![no_std]
#![feature(alloc_error_handler)]
extern crate alloc;
use alloc::vec::Vec;
use linked_list_allocator::LockedHeap;
// Global allocator
#[global_allocator]
static ALLOCATOR: LockedHeap = LockedHeap::empty();
// Khởi tạo heap (gọi một lần khi boot)
pub fn init_heap() {
// Vùng nhớ dành cho heap
const HEAP_SIZE: usize = 16 * 1024; // 16 KB
static mut HEAP_MEMORY: [u8; HEAP_SIZE] = [0; HEAP_SIZE];
unsafe {
ALLOCATOR.lock().init(
HEAP_MEMORY.as_ptr() as usize,
HEAP_SIZE
);
}
}
// Alloc error handler
#[alloc_error_handler]
fn alloc_error(_layout: core::alloc::Layout) -> ! {
panic!("Heap allocation failed!");
}
// Bây giờ có thể dùng Vec, String, Box...
fn use_heap() {
let mut vec = Vec::new();
vec.push(1);
vec.push(2);
let boxed = alloc::boxed::Box::new(42);
}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
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
Simple Bump Allocator (Minimal Implementation)
rust
use core::alloc::{GlobalAlloc, Layout};
use core::cell::UnsafeCell;
use core::ptr;
/// Simple bump allocator - chỉ allocate, không free
pub struct BumpAllocator {
heap_start: UnsafeCell<usize>,
heap_end: usize,
next: UnsafeCell<usize>,
}
unsafe impl Sync for BumpAllocator {}
impl BumpAllocator {
pub const fn new(start: usize, size: usize) -> Self {
Self {
heap_start: UnsafeCell::new(start),
heap_end: start + size,
next: UnsafeCell::new(start),
}
}
}
unsafe impl GlobalAlloc for BumpAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let next = self.next.get();
let alloc_start = (*next + layout.align() - 1) & !(layout.align() - 1);
let alloc_end = alloc_start + layout.size();
if alloc_end > self.heap_end {
return ptr::null_mut(); // Out of memory
}
*next = alloc_end;
alloc_start as *mut u8
}
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
// Bump allocator không free memory
// Tất cả memory được giải phóng khi reset
}
}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
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
3. Panic Handlers
Basic Panic Handler
rust
use core::panic::PanicInfo;
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
// Trong môi trường thực, có thể:
// 1. Bật LED đỏ
// 2. Gửi message qua UART
// 3. Reset device
// 4. Enter debug mode
// Minimal: infinite loop
loop {
// Prevent optimization
core::hint::spin_loop();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Panic Handler với Debug Output
rust
use core::panic::PanicInfo;
use core::fmt::Write;
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
// Giả sử có UART writer
let mut uart = unsafe { UartWriter::new() };
writeln!(uart, "PANIC!").ok();
if let Some(location) = info.location() {
writeln!(
uart,
" at {}:{}:{}",
location.file(),
location.line(),
location.column()
).ok();
}
if let Some(message) = info.message() {
writeln!(uart, " {}", message).ok();
}
// Halt
loop {
core::hint::spin_loop();
}
}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
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
Panic với RTT (Real-Time Transfer)
rust
use rtt_target::{rprintln, rtt_init_print};
use core::panic::PanicInfo;
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
rprintln!("PANIC: {}", info);
loop {}
}
fn main() {
rtt_init_print!();
rprintln!("Hello from embedded!");
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
4. Volatile Operations
Tại sao cần Volatile?
VOLATILE VS NORMAL ACCESS
┌─────────────────────────────────────────────────────────────────┐
│ │
│ Normal Read: │
│ ───────────── │
│ let x = *pointer; │
│ let y = *pointer; // Compiler có thể optimize: y = x │
│ │
│ Volatile Read: │
│ ─────────────── │
│ let x = read_volatile(pointer); │
│ let y = read_volatile(pointer); // PHẢI đọc lại từ memory │
│ │
│ WHY? Hardware registers có thể thay đổi bất cứ lúc nào! │
│ │
└─────────────────────────────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Volatile Read/Write
rust
use core::ptr::{read_volatile, write_volatile};
// Hardware register address
const GPIO_OUTPUT_REG: *mut u32 = 0x4000_1000 as *mut u32;
const GPIO_INPUT_REG: *const u32 = 0x4000_1004 as *const u32;
/// Đọc GPIO input pins
fn read_gpio() -> u32 {
// PHẢI dùng volatile vì hardware có thể thay đổi value
unsafe { read_volatile(GPIO_INPUT_REG) }
}
/// Set GPIO output pins
fn write_gpio(value: u32) {
// PHẢI dùng volatile để đảm bảo write thực sự xảy ra
unsafe { write_volatile(GPIO_OUTPUT_REG, value) }
}
/// Toggle LED (bit 0)
fn toggle_led() {
let current = read_gpio();
write_gpio(current ^ 0x01);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Volatile Wrapper Type
rust
use core::ptr::{read_volatile, write_volatile};
/// Wrapper cho volatile access
#[repr(transparent)]
pub struct Volatile<T: Copy> {
value: T,
}
impl<T: Copy> Volatile<T> {
pub fn read(&self) -> T {
unsafe { read_volatile(&self.value) }
}
pub fn write(&mut self, value: T) {
unsafe { write_volatile(&mut self.value, value) }
}
pub fn update<F>(&mut self, f: F)
where
F: FnOnce(T) -> T,
{
let value = self.read();
self.write(f(value));
}
}
// Usage
#[repr(C)]
struct GpioRegs {
output: Volatile<u32>,
input: Volatile<u32>,
direction: Volatile<u32>,
}
fn configure_gpio() {
let gpio = unsafe { &mut *(0x4000_1000 as *mut GpioRegs) };
gpio.direction.write(0xFF); // All outputs
gpio.output.write(0x01); // LED on
let input = gpio.input.read();
}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
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
5. Linker Scripts Basics
Memory Layout Definition
ld
/* linker.ld */
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 256K
RAM : ORIGIN = 0x20000000, LENGTH = 64K
}
SECTIONS
{
/* Code goes in Flash */
.text :
{
*(.text.reset_handler) /* Entry point first */
*(.text*) /* All other code */
} > FLASH
/* Read-only data */
.rodata :
{
*(.rodata*)
} > FLASH
/* Initialized data (copy from Flash to RAM) */
.data :
{
_sdata = .;
*(.data*)
_edata = .;
} > RAM AT > FLASH
/* Zero-initialized data */
.bss :
{
_sbss = .;
*(.bss*)
_ebss = .;
} > RAM
/* Stack at end of RAM */
_stack_top = ORIGIN(RAM) + LENGTH(RAM);
}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
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
Rust Build Configuration
toml
# .cargo/config.toml
[target.thumbv7em-none-eabihf]
rustflags = [
"-C", "link-arg=-Tlinker.ld",
"-C", "link-arg=--nmagic",
]
[build]
target = "thumbv7em-none-eabihf"1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
🎯 no_std Checklist
rust
#![no_std]
#![no_main]
// ✅ 1. Panic handler
#[panic_handler]
fn panic(_: &PanicInfo) -> ! { loop {} }
// ✅ 2. Entry point
#[no_mangle]
pub extern "C" fn _start() -> ! { loop {} }
// ✅ 3. (Optional) Allocator cho alloc crate
#[global_allocator]
static ALLOC: MyAllocator = MyAllocator::new();
// ✅ 4. (Optional) Alloc error handler
#[alloc_error_handler]
fn alloc_error(_: Layout) -> ! { panic!() }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
💡 DEVELOPMENT TIP
Sử dụng cargo build --release cho embedded — debug builds thường quá lớn cho Flash memory.