Skip to main content

tinyboot_ch32_hal/
boot_request.rs

1//! Boot request signaling and boot mode selection.
2//!
3//! Three schemes selected automatically by chip capabilities and flash mode:
4//!
5//! - **reg**: `BOOT_MODE` register (system-flash, chips without `boot_pin`)
6//! - **ram**: RAM magic word (user-flash, all chips)
7//! - **gpio**: RAM magic word + GPIO pin (system-flash, chips with `boot_pin`)
8//!
9//! Scheme cfgs (`boot_req_reg`, `boot_req_ram`, `boot_req_gpio`) are set by
10//! the build script. `ram` and `gpio` are both active for system-flash + boot_pin.
11
12// ── Configuration ──────────────────────────────────────────────────────
13
14/// Boot control hardware configuration.
15///
16/// For the gpio scheme (system-flash on chips with `boot_pin`), this
17/// configures the GPIO pin connected to the BOOT0 control circuit.
18/// For all other schemes, no hardware configuration is needed.
19#[cfg(boot_req_gpio)]
20#[derive(Copy, Clone)]
21pub struct Config {
22    /// GPIO pin connected to the BOOT0 control circuit.
23    pub pin: crate::Pin,
24    /// `true` if driving HIGH selects system flash (default for RC circuit).
25    pub active_high: bool,
26}
27
28/// Boot control hardware configuration (no-op for reg and ram schemes).
29#[cfg(not(boot_req_gpio))]
30#[derive(Copy, Clone, Default)]
31pub struct Config;
32
33// ── Public API ─────────────────────────────────────────────────────────
34
35/// Initialize boot control hardware. Call once at startup.
36///
37/// For the gpio scheme, configures the BOOT_CTL pin as push-pull output
38/// and defaults it to the system-flash direction. For other schemes this
39/// is a no-op.
40pub fn init(_config: &Config) {
41    #[cfg(boot_req_gpio)]
42    {
43        use crate::gpio::{self, PinMode};
44        crate::rcc::enable_gpio(_config.pin.port_index());
45        gpio::configure(_config.pin, PinMode::OUTPUT_PUSH_PULL);
46        drive_boot_pin(_config.pin, _config.active_high, true);
47    }
48}
49
50/// Check whether a boot-to-bootloader request is pending.
51pub fn is_boot_requested() -> bool {
52    #[cfg(boot_req_reg)]
53    return crate::flash::boot_mode();
54
55    #[cfg(boot_req_ram)]
56    return unsafe { core::ptr::read_volatile(&raw const __tb_boot_request) == BOOT_REQUEST_MAGIC };
57}
58
59/// Signal (or clear) boot intent for the next reset.
60pub fn set_boot_request(_config: &Config, request: bool) {
61    #[cfg(boot_req_reg)]
62    crate::flash::set_boot_mode(request);
63
64    #[cfg(boot_req_ram)]
65    {
66        let val = if request { BOOT_REQUEST_MAGIC } else { 0 };
67        unsafe { core::ptr::write_volatile(&raw mut __tb_boot_request, val) };
68    }
69
70    #[cfg(boot_req_gpio)]
71    drive_boot_pin(_config.pin, _config.active_high, request);
72}
73
74// ── Private ────────────────────────────────────────────────────────────
75
76#[cfg(boot_req_ram)]
77const BOOT_REQUEST_MAGIC: u32 = 0xB007_CAFE;
78
79#[cfg(boot_req_ram)]
80unsafe extern "C" {
81    static mut __tb_boot_request: u32;
82}
83
84#[cfg(boot_req_gpio)]
85fn drive_boot_pin(pin: crate::Pin, active_high: bool, system_flash: bool) {
86    use crate::gpio::{self, Level};
87    let level = if active_high == system_flash {
88        Level::High
89    } else {
90        Level::Low
91    };
92    gpio::set_level(pin, level);
93}