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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//! WS63 chip-specific PAC re-export and configuration.
pub use ExternalInterrupt as Interrupt;
/// Interrupt fired by the TIMER channel the embassy-time driver uses for alarms.
///
/// WS63 routes TIMER channel 0 to `TIMER_INT0` (IRQ 26, a standard `mie`-bit
/// local interrupt). The `embassy` time-driver and the app's trap handler reach
/// this through [`crate::soc::chip::ALARM_INTERRUPT`] so the alarm wiring is
/// chip-neutral (BS2X uses a different IRQ — see `soc/bs21.rs`).
pub const ALARM_INTERRUPT: Interrupt = TIMER_INT0;
/// System clock frequency (240 MHz).
///
/// # Clock initialization
///
/// **Boot ROM configures the CPU PLL to 240 MHz before loading the application.**
///
/// The fbb_ws63 flashboot bootloader sequence:
/// 1. `boot_clock_adapt()` — detects TCXO (24/40 MHz) via HW_CTL bit[0],
/// configures UART baud base and WDT tick counter to match TCXO freq
/// 2. `switch_flash_clock_to_pll()` — sets CLDO_CRG_CLK_SEL bit[18] to switch
/// the flash/SFC controller clock from TCXO to PLL
/// 3. Jumps to application entry point
///
/// **What the application must do:** switch peripheral clocks (UART, SPI, I2C)
/// from TCXO to PLL source. Use `clock_init::init_clocks()` for this.
///
/// If the PLL is NOT locked (unlikely, indicates hardware issue), the CPU
/// runs from the TCXO at 24 or 40 MHz. All timing calculations will be wrong.
/// Call `clock_init::probe_clocks()` to verify.
pub const SYSTEM_CLOCK_HZ: u32 = 240_000_000;
/// TCXO crystal frequency (24 MHz on standard WS63 modules).
///
/// This is the **counting clock of the Timer and Watchdog peripherals and the
/// TCXO time base** — NOT the 240 MHz CPU/PLL clock ([`SYSTEM_CLOCK_HZ`]).
///
/// The vendor SDK programs both the timer and the WDT to the TCXO crystal in
/// `clock_init` — `timer_porting_clock_value_set(REQ_24M)` and
/// `watchdog_port_set_clock(REQ_24M)` (fbb_ws63 `clock_init.c`), where
/// `REQ_24M = 24_000_000`. The 32 MHz `CONFIG_TIMER_CLOCK_VALUE` Kconfig default
/// and the 24 MHz "FPGA" WDT note are placeholders the silicon path overrides to
/// this value. ch2 of ws63-guide lists Timer = "晶体分频" (crystal-derived).
///
/// 40 MHz-crystal boards run the timer/WDT at 40 MHz instead; override there.
pub const TCXO_HZ: u32 = 24_000_000;
/// Timer / Watchdog counting clock (= [`TCXO_HZ`], 24 MHz).
///
/// Use this — **not** [`SYSTEM_CLOCK_HZ`] — to convert microseconds/milliseconds
/// to Timer or WDT counter ticks. The timer is clocked by the crystal, so at
/// 24 MHz one microsecond is 24 ticks (the vendor `timer_porting_us_2_cycle`
/// computes `us * (clock / 1_000_000)` = `us * 24`).
pub const TIMER_CLOCK_HZ: u32 = TCXO_HZ;
/// UART baud-base clock (160 MHz, PLL-derived).
///
/// After boot, `clock_init` switches the UART clock from TCXO to PLL and sets the
/// baud base to `UART_PLL_CLOCK = 160_000_000` (fbb_ws63 `clock_init.c`); ch2 of
/// ws63-guide also lists UART = 160 MHz. The baud divisor is `clock / (16 * baud)`,
/// so this — not the 240 MHz CPU clock — is the divisor base.
///
/// **On-silicon caveat (2026-06-14, see hisi-riscv-rs#10/#15):** examples that do
/// NOT run the (XIP-unsafe) full `clock_init` inherit flashboot's UART clock, which
/// is NOT this PLL base — so their baud is wrong on real hardware. The boot UART
/// clock is the raw TCXO crystal (24 or 40 MHz), selected per board; use
/// [`crate::uart::UartClock::Boot`] for those examples.
pub const UART_CLOCK_HZ: u32 = 160_000_000;
/// Boot (pre-`clock_init`) UART baud base for a 24 MHz-TCXO board.
pub const UART_BOOT_CLOCK_24M_HZ: u32 = 24_000_000;
/// Boot (pre-`clock_init`) UART baud base for a 40 MHz-TCXO board.
pub const UART_BOOT_CLOCK_40M_HZ: u32 = 40_000_000;
/// Live flashboot UART baud base (the raw TCXO crystal, 24 or 40 MHz) for use
/// BEFORE `clock_init` runs.
///
/// flashboot's `boot_clock_adapt()` sets the console UART baud base to the raw
/// TCXO — NOT the 160 MHz PLL — keyed on the `HW_CTL` TCXO-detect strap (fbb_ws63
/// `soc_porting.c` `get_tcxo_freq`/`boot_clock_adapt`). This mirrors that read.
/// **Confirmed on silicon (2026-06-14):** a 40 MHz board reads `HW_CTL = 0x2`
/// (bit 0 clear), and at 115200 8N1 that yields the measured flashboot divider
/// `div = 21, div_fra = 44` — the divider the HAL must reproduce for examples
/// that skip `clock_init` (hisi-riscv-rs#10/#15).
///
/// [`crate::uart::UartClock::Boot`] uses this value so the divider is computed
/// against the real base.
/// SPI controller input clock / SSI_CLK (160 MHz, PLL-derived).
///
/// SCK = SSI_CLK / SCKDV. WS63 derives the SPI clock from the 480 MHz FNPLL tap via
/// a two-stage divider: a CLDO_CRG divider sets SSI_CLK (= this value, 480/3), then
/// the in-controller SCKDV divides to SCK. `spi.rs::configure_spi_source_clock`
/// programs the CRG divider (`DIV_CTL3`) + switches the source to PLL on init —
/// mirroring the vendor `spi_porting_clock_init` — so this SSI_CLK is established,
/// not just assumed. ch2 of ws63-guide lists SPI = 160 MHz; the QEMU model matches.
pub const SPI_CLOCK_HZ: u32 = 160_000_000;
/// I2C peripheral clock (= [`TCXO_HZ`], 24 MHz).
///
/// Unlike UART/SPI, the I2C clock is **not** switched to the PLL: `clock_init` sets
/// it to the TCXO crystal via `i2c_port_set_clock_value(REQ_24M)` (fbb_ws63
/// `clock_init.c`), and the SCL divisor math (`SCL = clock / (2*(scl_h+scl_l)*2)`)
/// is computed against this value. (ch2's nominal "I2C = 80 MHz" is the bus-capability
/// figure, not the divisor base the SDK uses.)
pub const I2C_CLOCK_HZ: u32 = TCXO_HZ;
/// Number of GPIO pins (19: GPIO0[7:0] + GPIO1[15:8] + GPIO2[18:16]).
pub const GPIO_COUNT: usize = 19;
/// Number of ULP GPIO pins (8: GPIO107-114).
pub const ULP_GPIO_COUNT: usize = 8;
/// Number of UART instances.
pub const UART_COUNT: usize = 3;
/// Number of I2C instances.
pub const I2C_COUNT: usize = 2;
/// Number of SPI instances.
pub const SPI_COUNT: usize = 2;
/// Number of PWM channels.
pub const PWM_CHANNEL_COUNT: usize = 8;
/// Number of DMA channels (per controller).
pub const DMA_CHANNEL_COUNT: usize = 4;
/// Number of TIMER instances.
pub const TIMER_COUNT: usize = 3;
/// Number of LSADC channels.
pub const LSADC_CHANNEL_COUNT: usize = 6;
/// TCXO counter width in bits.
pub const TCXO_COUNTER_WIDTH: usize = 64;
/// RTC counter width in bits.
pub const RTC_COUNTER_WIDTH: usize = 48;