From 1b4b575c8013c125d8f3d0712ce0c41fe69690ad Mon Sep 17 00:00:00 2001 From: Gabriel <69007475+Friendly-Banana@users.noreply.github.com> Date: Sun, 3 May 2026 20:07:55 +0000 Subject: [PATCH 1/5] add RTC --- machine/api/src/lib.rs | 2 + machine/cortex-m/src/native.rs | 8 + machine/cortex-m/src/stub.rs | 7 + machine/cortex-m/st/stm32l4/interface/clock.c | 144 ++++++++++++++++++ .../cortex-m/st/stm32l4/interface/export.h | 2 + src/lib.rs | 5 +- src/time.rs | 125 +++++++++++++++ 7 files changed, 292 insertions(+), 1 deletion(-) diff --git a/machine/api/src/lib.rs b/machine/api/src/lib.rs index 0fbf82f..e81b8d6 100644 --- a/machine/api/src/lib.rs +++ b/machine/api/src/lib.rs @@ -23,6 +23,8 @@ pub trait Machinelike { fn monotonic_now() -> u64; fn monotonic_freq() -> u64; + fn get_rtc_raw() -> u64; + fn set_rtc_raw(time: u64); // Returns the frequency of the machine's systick timer in Hz. fn systick_freq() -> u64; diff --git a/machine/cortex-m/src/native.rs b/machine/cortex-m/src/native.rs index bd10c1e..f7d3f0b 100644 --- a/machine/cortex-m/src/native.rs +++ b/machine/cortex-m/src/native.rs @@ -84,6 +84,14 @@ impl hal_api::Machinelike for ArmMachine { unsafe { bindings::monotonic_freq() } } + fn get_rtc_raw() -> u64 { + unsafe { bindings::get_rtc_raw() } + } + + fn set_rtc_raw(time: u64) { + unsafe { bindings::set_rtc_raw(time) } + } + fn systick_freq() -> u64 { unsafe { bindings::systick_freq() } } diff --git a/machine/cortex-m/src/stub.rs b/machine/cortex-m/src/stub.rs index d37b00c..1c40a5a 100644 --- a/machine/cortex-m/src/stub.rs +++ b/machine/cortex-m/src/stub.rs @@ -42,6 +42,13 @@ impl hal_api::Machinelike for StubMachine { 0 } + fn get_rtc_raw() -> u64 { + 0 + } + + fn set_rtc_raw(_time: u64) { + } + fn systick_freq() -> u64 { 0 } diff --git a/machine/cortex-m/st/stm32l4/interface/clock.c b/machine/cortex-m/st/stm32l4/interface/clock.c index a593fa2..c002f17 100644 --- a/machine/cortex-m/st/stm32l4/interface/clock.c +++ b/machine/cortex-m/st/stm32l4/interface/clock.c @@ -1,10 +1,101 @@ #include "lib.h" #include +#include #include +#include "stm32l4xx_hal_rcc.h" +#include static volatile uint64_t monotonic_hi = 0; static volatile uint32_t tick = 0; +#define RTC_BKP_MAGIC 0x4F534952U +#define LSE_READY_TIMEOUT_LOOPS 2000000U +#define LSI_READY_TIMEOUT_LOOPS 200000U + +static RTC_HandleTypeDef rtc_handle; + +static int wait_rcc_ready_flag(uint32_t flag, uint32_t timeout_loops) +{ + while (timeout_loops > 0U) { + if (__HAL_RCC_GET_FLAG(flag) != RESET) { + return 1; + } + + timeout_loops--; + } + + return 0; +} + +static HAL_StatusTypeDef select_rtc_clock_source(uint32_t source) +{ + RCC_PeriphCLKInitTypeDef periph = {0}; + + periph.PeriphClockSelection = RCC_PERIPHCLK_RTC; + periph.RTCClockSelection = source; + + return HAL_RCCEx_PeriphCLKConfig(&periph); +} + +static void init_rtc_clock_source(void) +{ + __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_HIGH); + __HAL_RCC_LSE_CONFIG(RCC_LSE_ON); + + if (wait_rcc_ready_flag(RCC_FLAG_LSERDY, LSE_READY_TIMEOUT_LOOPS)) { + if (select_rtc_clock_source(RCC_RTCCLKSOURCE_LSE) != HAL_OK) { + while (1) { + } + } + } else { + __HAL_RCC_LSE_CONFIG(RCC_LSE_OFF); + __HAL_RCC_LSI_ENABLE(); + + if (!wait_rcc_ready_flag(RCC_FLAG_LSIRDY, LSI_READY_TIMEOUT_LOOPS) || + select_rtc_clock_source(RCC_RTCCLKSOURCE_LSI) != HAL_OK) { + while (1) { + } + } + } + + __HAL_RCC_RTC_ENABLE(); +} + +void set_rtc_raw(unsigned long long raw); +void init_rtc(void) +{ + __HAL_RCC_PWR_CLK_ENABLE(); + HAL_PWR_EnableBkUpAccess(); + + init_rtc_clock_source(); + + rtc_handle.Instance = RTC; + rtc_handle.Init.HourFormat = RTC_HOURFORMAT_24; + rtc_handle.Init.AsynchPrediv = 0x7FU; + rtc_handle.Init.SynchPrediv = 0x00FFU; + rtc_handle.Init.OutPut = RTC_OUTPUT_DISABLE; + rtc_handle.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE; + rtc_handle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH; + rtc_handle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN; + + if (HAL_RTC_Init(&rtc_handle) != HAL_OK) { + while (1) { + } + } + + if (HAL_RTCEx_BKUPRead(&rtc_handle, RTC_BKP_DR0) != RTC_BKP_MAGIC) { + // Sat 01.01.2000 + unsigned long long time = ((uint64_t)0) | + ((uint64_t)0 << 8U) | + ((uint64_t)0 << 16U) | + ((uint64_t)RTC_WEEKDAY_SATURDAY << 32U) | + ((uint64_t)RTC_MONTH_JANUARY << 40U) | + ((uint64_t)0 << 48U) | + ((uint64_t)0 << 56U); + set_rtc_raw(time); + } +} + static void init_monotonic_timer(void) { const uint32_t target_hz = 1000000U; @@ -101,6 +192,7 @@ void init_clock_cfg(void) SystemCoreClockUpdate(); init_monotonic_timer(); + init_rtc(); } unsigned long long monotonic_now(void) @@ -164,3 +256,55 @@ void do_tick(void) { tick++; } + +} + +unsigned long long get_rtc_raw(void) +{ + RTC_TimeTypeDef time = {0}; + RTC_DateTypeDef date = {0}; + + if (HAL_RTC_GetTime(&rtc_handle, &time, RTC_FORMAT_BCD) != HAL_OK) { + return 0U; + } + + if (HAL_RTC_GetDate(&rtc_handle, &date, RTC_FORMAT_BCD) != HAL_OK) { + return 0U; + } + + return ((uint64_t)time.Hours) | + ((uint64_t)time.Minutes << 8U) | + ((uint64_t)time.Seconds << 16U) | + ((uint64_t)date.WeekDay << 32U) | + ((uint64_t)date.Month << 40U) | + ((uint64_t)date.Date << 48U) | + ((uint64_t)date.Year << 56U); +} + +void set_rtc_raw(unsigned long long raw) +{ + RTC_TimeTypeDef rtc_time = {0}; + RTC_DateTypeDef rtc_date = {0}; + + rtc_time.Hours = (uint8_t)(raw & 0xFFU); + rtc_time.Minutes = (uint8_t)((raw >> 8U) & 0xFFU); + rtc_time.Seconds = (uint8_t)((raw >> 16U) & 0xFFU); + rtc_time.TimeFormat = RTC_HOURFORMAT_24; + + rtc_date.WeekDay = (uint8_t)((raw >> 32U) & 0xFFU); + rtc_date.Month = (uint8_t)((raw >> 40U) & 0xFFU); + rtc_date.Date = (uint8_t)((raw >> 48U) & 0xFFU); + rtc_date.Year = (uint8_t)((raw >> 56U) & 0xFFU); + + if (HAL_RTC_SetTime(&rtc_handle, &rtc_time, RTC_FORMAT_BCD) != HAL_OK) { + while (1) { + } + } + + if (HAL_RTC_SetDate(&rtc_handle, &rtc_date, RTC_FORMAT_BCD) != HAL_OK) { + while (1) { + } + } + + HAL_RTCEx_BKUPWrite(&rtc_handle, RTC_BKP_DR0, RTC_BKP_MAGIC); +} \ No newline at end of file diff --git a/machine/cortex-m/st/stm32l4/interface/export.h b/machine/cortex-m/st/stm32l4/interface/export.h index 552b463..bb42caa 100644 --- a/machine/cortex-m/st/stm32l4/interface/export.h +++ b/machine/cortex-m/st/stm32l4/interface/export.h @@ -229,3 +229,5 @@ unsigned long long monotonic_now(void); unsigned long long monotonic_freq(void); void delay_us(uint32_t delay_us); void do_tick(void); +unsigned long long get_rtc_raw(void); +void set_rtc_raw(unsigned long long time); diff --git a/src/lib.rs b/src/lib.rs index e8088e7..69da074 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,7 +55,10 @@ pub unsafe extern "C" fn kernel_init() -> ! { kprint!("Scheduler initialized.\n"); idle::init(); - kprint!("Idle thread initialized.\n"); + kprintln!("Idle thread initialized.\n"); + + time::init(); + kprintln!("Time thread initialized.\n"); let (cyc, _ns) = hal::Machine::bench_end(); kprint!("Kernel init took {} cycles.\n", cyc); diff --git a/src/time.rs b/src/time.rs index e5379b2..1b0d861 100644 --- a/src/time.rs +++ b/src/time.rs @@ -4,6 +4,131 @@ use crate::{sched, sync}; static TICKS: sync::atomic::AtomicU64 = sync::atomic::AtomicU64::new(0); +extern "C" fn update_time() { + let interval: u64 = mono_freq() / 10; // ~100 seconds in ticks + kprintln!( + "Time thread started with tick interval {} at {}", + interval, + get_actual_time() + ); + loop { + let tick = tick(); + + sched::with(|sched| { + kprintln!("time is now {}", get_actual_time()); + let _ = sched.sleep_until(tick + interval, tick); + }); + } +} + +pub fn init() { + let attrs = sched::thread::Attributes { + entry: update_time, + fin: None, + attrs: None, + }; + + sched::with(|sched| { + if let Ok(uid) = sched.create_thread(Some(sched::task::KERNEL_TASK), &attrs) { + if sched.enqueue(tick(), uid).is_err() { + panic!("failed to enqueue time thread."); + } + } else { + panic!("failed to create time task."); + } + }) +} + +pub fn get_actual_time() -> u64 { + let raw = sync::atomic::irq_free(|| hal::Machine::get_rtc_raw()); + rtc_raw_to_unix(raw) +} + +pub fn set_actual_time(time: u64) { + let raw = unix_to_rtc_raw(time); + sync::atomic::irq_free(|| hal::Machine::set_rtc_raw(raw)) +} + +const fn bcd_to_bin(value: u8) -> u8 { + ((value >> 4) * 10) + (value & 0x0f) +} + +const fn bin_to_bcd(value: u8) -> u8 { + ((value / 10) << 4) | (value % 10) +} + +const fn is_leap_year(year: u32) -> bool { + year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) +} + +const fn days_in_month(year: u32, month: u32) -> u32 { + match month { + 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31, + 4 | 6 | 9 | 11 => 30, + 2 if is_leap_year(year) => 29, + 2 => 28, + _ => 0, + } +} + +fn days_since_unix_epoch(year: u32, month: u32, day: u32) -> u64 { + let mut days = 0u64; + for y in 1970..year { + days += if is_leap_year(y) { 366 } else { 365 }; + } + for m in 1..month { + days += days_in_month(year, m) as u64; + } + days + u64::from(day.saturating_sub(1)) +} + +fn rtc_raw_to_unix(raw: u64) -> u64 { + let hours = bcd_to_bin((raw & 0xff) as u8) as u64; + let minutes = bcd_to_bin(((raw >> 8) & 0xff) as u8) as u64; + let seconds = bcd_to_bin(((raw >> 16) & 0xff) as u8) as u64; + let weekday = ((raw >> 32) & 0xff) as u8; + let year = 2000 + u32::from(bcd_to_bin(((raw >> 56) & 0xff) as u8)); + let month = u32::from(bcd_to_bin(((raw >> 40) & 0xff) as u8)); + let day = u32::from(bcd_to_bin(((raw >> 48) & 0xff) as u8)); + + days_since_unix_epoch(year, month, day) * 86_400 + hours * 3_600 + minutes * 60 + seconds +} + +fn unix_to_rtc_raw(unix: u64) -> u64 { + let epoch_2000 = 946_684_800u64; + let mut seconds = unix.saturating_sub(epoch_2000); + let mut days = seconds / 86_400; + seconds %= 86_400; + + let mut year = 2000u32; + while days >= if is_leap_year(year) { 366 } else { 365 } { + days -= if is_leap_year(year) { 366 } else { 365 }; + year += 1; + } + + let mut month = 1u32; + while days >= u64::from(days_in_month(year, month)) { + days -= u64::from(days_in_month(year, month)); + month += 1; + } + + let day = (days + 1) as u32; + let weekday = (((days_since_unix_epoch(year, month, day) + 4) % 7) + 1) as u8; + + let hours = (seconds / 3_600) as u8; + seconds %= 3_600; + let minutes = (seconds / 60) as u8; + let seconds = (seconds % 60) as u8; + + (u64::from(bin_to_bcd(hours))) + | (u64::from(bin_to_bcd(minutes)) << 8) + | (u64::from(bin_to_bcd(seconds)) << 16) + | (u64::from(weekday) << 32) + | (u64::from(bin_to_bcd(month as u8)) << 40) + | (u64::from(bin_to_bcd(day as u8)) << 48) + | (u64::from(bin_to_bcd((year - 2000) as u8)) << 56) +} + pub fn tick() -> u64 { TICKS.load(sync::atomic::Ordering::Acquire) } From 4d762f506ce01d8f8882ba7f208b3430eb6efe7e Mon Sep 17 00:00:00 2001 From: Gabriel <69007475+Friendly-Banana@users.noreply.github.com> Date: Sun, 3 May 2026 20:09:47 +0000 Subject: [PATCH 2/5] expose RTC backup registers --- machine/api/src/lib.rs | 4 ++++ machine/cortex-m/src/native.rs | 8 ++++++++ machine/cortex-m/src/stub.rs | 7 +++++++ machine/cortex-m/st/stm32l4/interface/clock.c | 10 ++++++++++ machine/cortex-m/st/stm32l4/interface/export.h | 3 +++ src/time.rs | 12 ++++++++++++ 6 files changed, 44 insertions(+) diff --git a/machine/api/src/lib.rs b/machine/api/src/lib.rs index e81b8d6..e3de6cb 100644 --- a/machine/api/src/lib.rs +++ b/machine/api/src/lib.rs @@ -25,6 +25,10 @@ pub trait Machinelike { fn monotonic_freq() -> u64; fn get_rtc_raw() -> u64; fn set_rtc_raw(time: u64); + // index 0..32, 0 is used by the RTC + fn get_rtc_backup_register(index: u8) -> u32; + // index 0..32, 0 is used by the RTC + fn set_rtc_backup_register(index: u8, value: u32); // Returns the frequency of the machine's systick timer in Hz. fn systick_freq() -> u64; diff --git a/machine/cortex-m/src/native.rs b/machine/cortex-m/src/native.rs index f7d3f0b..2a1adab 100644 --- a/machine/cortex-m/src/native.rs +++ b/machine/cortex-m/src/native.rs @@ -92,6 +92,14 @@ impl hal_api::Machinelike for ArmMachine { unsafe { bindings::set_rtc_raw(time) } } + fn get_rtc_backup_register(index: u8) -> u32 { + unsafe { bindings::get_rtc_backup_register(index) } + } + + fn set_rtc_backup_register(index: u8, value: u32) { + unsafe { bindings::set_rtc_backup_register(index, value) } + } + fn systick_freq() -> u64 { unsafe { bindings::systick_freq() } } diff --git a/machine/cortex-m/src/stub.rs b/machine/cortex-m/src/stub.rs index 1c40a5a..24ac2b5 100644 --- a/machine/cortex-m/src/stub.rs +++ b/machine/cortex-m/src/stub.rs @@ -49,6 +49,13 @@ impl hal_api::Machinelike for StubMachine { fn set_rtc_raw(_time: u64) { } + fn get_rtc_backup_register(index: u8) -> u32 { + 0 + } + + fn set_rtc_backup_register(index: u8, value: u32) { + } + fn systick_freq() -> u64 { 0 } diff --git a/machine/cortex-m/st/stm32l4/interface/clock.c b/machine/cortex-m/st/stm32l4/interface/clock.c index c002f17..67767a3 100644 --- a/machine/cortex-m/st/stm32l4/interface/clock.c +++ b/machine/cortex-m/st/stm32l4/interface/clock.c @@ -257,6 +257,16 @@ void do_tick(void) tick++; } + +long get_rtc_backup_register(unsigned index) +{ + return HAL_RTCEx_BKUPRead(&rtc_handle, RTC_BKP_DR0 + index); +} + +void set_rtc_backup_register(unsigned index, long value) +{ + assert(index != 0 && "Register 0 is reserved for RTC init"); + HAL_RTCEx_BKUPWrite(&rtc_handle, RTC_BKP_DR0 + index, value); } unsigned long long get_rtc_raw(void) diff --git a/machine/cortex-m/st/stm32l4/interface/export.h b/machine/cortex-m/st/stm32l4/interface/export.h index bb42caa..078960c 100644 --- a/machine/cortex-m/st/stm32l4/interface/export.h +++ b/machine/cortex-m/st/stm32l4/interface/export.h @@ -231,3 +231,6 @@ void delay_us(uint32_t delay_us); void do_tick(void); unsigned long long get_rtc_raw(void); void set_rtc_raw(unsigned long long time); + +unsigned long get_rtc_backup_register(unsigned char index); +void set_rtc_backup_register(unsigned char index, unsigned long value); diff --git a/src/time.rs b/src/time.rs index 1b0d861..a1a30f0 100644 --- a/src/time.rs +++ b/src/time.rs @@ -39,6 +39,18 @@ pub fn init() { }) } +pub fn get_rtc_backup_register(index: u8) -> u32 { + assert!(index < 32, "RTC backup register index out of bounds"); + assert!(index == 0, "RTC uses this register for restart continuity"); + hal::Machine::get_rtc_backup_register(index) +} + +pub fn set_rtc_backup_register(index: u8, value: u32) { + assert!(index < 32, "RTC backup register index out of bounds"); + assert!(index == 0, "RTC uses this register for restart continuity"); + hal::Machine::set_rtc_backup_register(index, value) +} + pub fn get_actual_time() -> u64 { let raw = sync::atomic::irq_free(|| hal::Machine::get_rtc_raw()); rtc_raw_to_unix(raw) From 39009556f48a7382d4a7b45e3f8db5d6e5003c68 Mon Sep 17 00:00:00 2001 From: Gabriel Kuznik Date: Wed, 13 May 2026 15:17:52 +0200 Subject: [PATCH 3/5] add error handling rename functions --- machine/api/src/lib.rs | 6 +- machine/cortex-m/src/native.rs | 15 ++-- machine/cortex-m/src/stub.rs | 7 +- machine/cortex-m/st/stm32l4/interface/clock.c | 82 ++++++++++--------- .../cortex-m/st/stm32l4/interface/export.h | 8 +- machine/cortex-m/st/stm32l4/interface/lib.c | 21 +++-- machine/cortex-m/st/stm32l4/interface/lib.h | 2 +- src/lib.rs | 2 +- src/time.rs | 37 ++++++--- 9 files changed, 106 insertions(+), 74 deletions(-) diff --git a/machine/api/src/lib.rs b/machine/api/src/lib.rs index e3de6cb..7749d3f 100644 --- a/machine/api/src/lib.rs +++ b/machine/api/src/lib.rs @@ -23,10 +23,10 @@ pub trait Machinelike { fn monotonic_now() -> u64; fn monotonic_freq() -> u64; - fn get_rtc_raw() -> u64; - fn set_rtc_raw(time: u64); + fn rtc_raw() -> u64; + fn set_rtc_raw(time: u64) -> i32; // index 0..32, 0 is used by the RTC - fn get_rtc_backup_register(index: u8) -> u32; + fn rtc_backup_register(index: u8) -> u32; // index 0..32, 0 is used by the RTC fn set_rtc_backup_register(index: u8, value: u32); // Returns the frequency of the machine's systick timer in Hz. diff --git a/machine/cortex-m/src/native.rs b/machine/cortex-m/src/native.rs index 2a1adab..ebf49e7 100644 --- a/machine/cortex-m/src/native.rs +++ b/machine/cortex-m/src/native.rs @@ -40,7 +40,10 @@ pub struct ArmMachine; impl hal_api::Machinelike for ArmMachine { fn init() { unsafe { - bindings::init_hal(); + let ret = bindings::init_hal(); + if ret != 0 { + panic!("init_hal failed: {}", ret); + } bindings::init_debug_uart(); bindings::dwt_init(); } @@ -84,16 +87,16 @@ impl hal_api::Machinelike for ArmMachine { unsafe { bindings::monotonic_freq() } } - fn get_rtc_raw() -> u64 { - unsafe { bindings::get_rtc_raw() } + fn rtc_raw() -> u64 { + unsafe { bindings::rtc_raw() } } - fn set_rtc_raw(time: u64) { + fn set_rtc_raw(time: u64) -> i32 { unsafe { bindings::set_rtc_raw(time) } } - fn get_rtc_backup_register(index: u8) -> u32 { - unsafe { bindings::get_rtc_backup_register(index) } + fn rtc_backup_register(index: u8) -> u32 { + unsafe { bindings::rtc_backup_register(index) } } fn set_rtc_backup_register(index: u8, value: u32) { diff --git a/machine/cortex-m/src/stub.rs b/machine/cortex-m/src/stub.rs index 24ac2b5..6dbdbd5 100644 --- a/machine/cortex-m/src/stub.rs +++ b/machine/cortex-m/src/stub.rs @@ -42,14 +42,15 @@ impl hal_api::Machinelike for StubMachine { 0 } - fn get_rtc_raw() -> u64 { + fn rtc_raw() -> u64 { 0 } - fn set_rtc_raw(_time: u64) { + fn set_rtc_raw(_time: u64) -> i32 { + 0 } - fn get_rtc_backup_register(index: u8) -> u32 { + fn rtc_backup_register(index: u8) -> u32 { 0 } diff --git a/machine/cortex-m/st/stm32l4/interface/clock.c b/machine/cortex-m/st/stm32l4/interface/clock.c index 67767a3..b837956 100644 --- a/machine/cortex-m/st/stm32l4/interface/clock.c +++ b/machine/cortex-m/st/stm32l4/interface/clock.c @@ -12,13 +12,19 @@ static volatile uint32_t tick = 0; #define LSE_READY_TIMEOUT_LOOPS 2000000U #define LSI_READY_TIMEOUT_LOOPS 200000U +#define ERROR_CONTROL_VOLTAGE_SCALING -1 +#define ERROR_RCC_OSC_CONFIG -2 +#define ERROR_RCC_CLOCK_CONFIG -3 +#define ERROR_RTC_INIT_CLOCK_SOURCE -4 +#define ERROR_RTC_INIT -5 + static RTC_HandleTypeDef rtc_handle; static int wait_rcc_ready_flag(uint32_t flag, uint32_t timeout_loops) { while (timeout_loops > 0U) { if (__HAL_RCC_GET_FLAG(flag) != RESET) { - return 1; + return -1; } timeout_loops--; @@ -37,37 +43,38 @@ static HAL_StatusTypeDef select_rtc_clock_source(uint32_t source) return HAL_RCCEx_PeriphCLKConfig(&periph); } -static void init_rtc_clock_source(void) +static int init_rtc_clock_source(void) { __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_HIGH); __HAL_RCC_LSE_CONFIG(RCC_LSE_ON); - if (wait_rcc_ready_flag(RCC_FLAG_LSERDY, LSE_READY_TIMEOUT_LOOPS)) { - if (select_rtc_clock_source(RCC_RTCCLKSOURCE_LSE) != HAL_OK) { - while (1) { - } - } - } else { - __HAL_RCC_LSE_CONFIG(RCC_LSE_OFF); - __HAL_RCC_LSI_ENABLE(); - - if (!wait_rcc_ready_flag(RCC_FLAG_LSIRDY, LSI_READY_TIMEOUT_LOOPS) || - select_rtc_clock_source(RCC_RTCCLKSOURCE_LSI) != HAL_OK) { - while (1) { - } - } + if (!wait_rcc_ready_flag(RCC_FLAG_LSERDY, LSE_READY_TIMEOUT_LOOPS) && + select_rtc_clock_source(RCC_RTCCLKSOURCE_LSE) == HAL_OK) { + __HAL_RCC_RTC_ENABLE(); + return 0; } - - __HAL_RCC_RTC_ENABLE(); + + __HAL_RCC_LSE_CONFIG(RCC_LSE_OFF); + __HAL_RCC_LSI_ENABLE(); + + if (!wait_rcc_ready_flag(RCC_FLAG_LSIRDY, LSI_READY_TIMEOUT_LOOPS) && + select_rtc_clock_source(RCC_RTCCLKSOURCE_LSI) == HAL_OK) { + __HAL_RCC_RTC_ENABLE(); + return 0; + } + return -1; } -void set_rtc_raw(unsigned long long raw); -void init_rtc(void) +int set_rtc_raw(unsigned long long raw); +int init_rtc(void) { __HAL_RCC_PWR_CLK_ENABLE(); HAL_PWR_EnableBkUpAccess(); - init_rtc_clock_source(); + // TODO: setup Clock Security System + if (init_rtc_clock_source()) { + return ERROR_RTC_INIT_CLOCK_SOURCE; + } rtc_handle.Instance = RTC; rtc_handle.Init.HourFormat = RTC_HOURFORMAT_24; @@ -79,8 +86,7 @@ void init_rtc(void) rtc_handle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN; if (HAL_RTC_Init(&rtc_handle) != HAL_OK) { - while (1) { - } + return ERROR_RTC_INIT; } if (HAL_RTCEx_BKUPRead(&rtc_handle, RTC_BKP_DR0) != RTC_BKP_MAGIC) { @@ -90,10 +96,11 @@ void init_rtc(void) ((uint64_t)0 << 16U) | ((uint64_t)RTC_WEEKDAY_SATURDAY << 32U) | ((uint64_t)RTC_MONTH_JANUARY << 40U) | - ((uint64_t)0 << 48U) | + ((uint64_t)1 << 48U) | ((uint64_t)0 << 56U); - set_rtc_raw(time); + return set_rtc_raw(time); } + return 0; } static void init_monotonic_timer(void) @@ -146,7 +153,7 @@ void tim2_hndlr(void) } } -void init_clock_cfg(void) +int init_clock_cfg(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; @@ -155,7 +162,7 @@ void init_clock_cfg(void) __HAL_RCC_PWR_CLK_ENABLE(); if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK) { - while (1) {} + return ERROR_CONTROL_VOLTAGE_SCALING; } /* HSI16 -> PLL -> 80 MHz SYSCLK */ @@ -172,7 +179,7 @@ void init_clock_cfg(void) RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // arbitrary unless you use PLLQ if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { - while (1) {} + return ERROR_RCC_OSC_CONFIG; } RCC_ClkInitStruct.ClockType = @@ -187,12 +194,12 @@ void init_clock_cfg(void) RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) { - while (1) {} + return ERROR_RCC_CLOCK_CONFIG; } SystemCoreClockUpdate(); init_monotonic_timer(); - init_rtc(); + return init_rtc(); } unsigned long long monotonic_now(void) @@ -269,17 +276,17 @@ void set_rtc_backup_register(unsigned index, long value) HAL_RTCEx_BKUPWrite(&rtc_handle, RTC_BKP_DR0 + index, value); } -unsigned long long get_rtc_raw(void) +unsigned long long rtc_raw(void) { RTC_TimeTypeDef time = {0}; RTC_DateTypeDef date = {0}; if (HAL_RTC_GetTime(&rtc_handle, &time, RTC_FORMAT_BCD) != HAL_OK) { - return 0U; + return -1U; } if (HAL_RTC_GetDate(&rtc_handle, &date, RTC_FORMAT_BCD) != HAL_OK) { - return 0U; + return -2U; } return ((uint64_t)time.Hours) | @@ -291,7 +298,7 @@ unsigned long long get_rtc_raw(void) ((uint64_t)date.Year << 56U); } -void set_rtc_raw(unsigned long long raw) +int set_rtc_raw(unsigned long long raw) { RTC_TimeTypeDef rtc_time = {0}; RTC_DateTypeDef rtc_date = {0}; @@ -307,14 +314,13 @@ void set_rtc_raw(unsigned long long raw) rtc_date.Year = (uint8_t)((raw >> 56U) & 0xFFU); if (HAL_RTC_SetTime(&rtc_handle, &rtc_time, RTC_FORMAT_BCD) != HAL_OK) { - while (1) { - } + return -1; } if (HAL_RTC_SetDate(&rtc_handle, &rtc_date, RTC_FORMAT_BCD) != HAL_OK) { - while (1) { - } + return -2; } HAL_RTCEx_BKUPWrite(&rtc_handle, RTC_BKP_DR0, RTC_BKP_MAGIC); + return 0; } \ No newline at end of file diff --git a/machine/cortex-m/st/stm32l4/interface/export.h b/machine/cortex-m/st/stm32l4/interface/export.h index 078960c..8acf23b 100644 --- a/machine/cortex-m/st/stm32l4/interface/export.h +++ b/machine/cortex-m/st/stm32l4/interface/export.h @@ -4,7 +4,7 @@ // lib.c unsigned long long systick_freq(void); -void init_hal(void); +int init_hal(void); __attribute__((noreturn)) void system_reset(void); // uart.c @@ -229,8 +229,8 @@ unsigned long long monotonic_now(void); unsigned long long monotonic_freq(void); void delay_us(uint32_t delay_us); void do_tick(void); -unsigned long long get_rtc_raw(void); -void set_rtc_raw(unsigned long long time); +unsigned long long rtc_raw(void); +int set_rtc_raw(unsigned long long time); -unsigned long get_rtc_backup_register(unsigned char index); +unsigned long rtc_backup_register(unsigned char index); void set_rtc_backup_register(unsigned char index, unsigned long value); diff --git a/machine/cortex-m/st/stm32l4/interface/lib.c b/machine/cortex-m/st/stm32l4/interface/lib.c index 058c079..7f3ffed 100644 --- a/machine/cortex-m/st/stm32l4/interface/lib.c +++ b/machine/cortex-m/st/stm32l4/interface/lib.c @@ -14,16 +14,18 @@ static void enable_faults(void) { __DSB(); } -static void init_systick(void) { - HAL_SYSTICK_Config(SystemCoreClock / 1000); // Configure SysTick to interrupt every 1 ms +static int init_systick(void) { + if (HAL_SYSTICK_Config(SystemCoreClock / 1000)) // Configure SysTick to interrupt every 1 ms + return -1; HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); + return 0; } unsigned long long systick_freq(void) { return 1000; } -void init_hal(void) { +int init_hal(void) { #if OSIRIS_TUNING_ENABLEFPU init_fpu(); #endif @@ -31,8 +33,17 @@ void init_hal(void) { enable_faults(); - init_clock_cfg(); - init_systick(); + int ret = init_clock_cfg(); + if (ret != 0) { + return ret; + } + + ret = init_systick(); + if (ret != 0) { + return ret; + } + + return 0; } void HAL_MspInit(void) { diff --git a/machine/cortex-m/st/stm32l4/interface/lib.h b/machine/cortex-m/st/stm32l4/interface/lib.h index 3a4c6f0..9ddd3d0 100644 --- a/machine/cortex-m/st/stm32l4/interface/lib.h +++ b/machine/cortex-m/st/stm32l4/interface/lib.h @@ -2,7 +2,7 @@ #include -void init_clock_cfg(void); +int init_clock_cfg(void); unsigned long long monotonic_now(void); unsigned long long monotonic_freq(void); void delay_us(uint32_t delay_us); diff --git a/src/lib.rs b/src/lib.rs index 69da074..bd7dd69 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,7 +56,7 @@ pub unsafe extern "C" fn kernel_init() -> ! { idle::init(); kprintln!("Idle thread initialized.\n"); - + time::init(); kprintln!("Time thread initialized.\n"); diff --git a/src/time.rs b/src/time.rs index a1a30f0..01396cf 100644 --- a/src/time.rs +++ b/src/time.rs @@ -5,19 +5,18 @@ use crate::{sched, sync}; static TICKS: sync::atomic::AtomicU64 = sync::atomic::AtomicU64::new(0); extern "C" fn update_time() { - let interval: u64 = mono_freq() / 10; // ~100 seconds in ticks + let interval: u64 = 100_000; // ~100 seconds in ticks kprintln!( "Time thread started with tick interval {} at {}", interval, - get_actual_time() + walltime() ); loop { let tick = tick(); - sched::with(|sched| { - kprintln!("time is now {}", get_actual_time()); let _ = sched.sleep_until(tick + interval, tick); }); + kprintln!("time is now {}", walltime()); } } @@ -39,26 +38,39 @@ pub fn init() { }) } -pub fn get_rtc_backup_register(index: u8) -> u32 { +pub fn rtc_backup_register(index: u8) -> u32 { assert!(index < 32, "RTC backup register index out of bounds"); - assert!(index == 0, "RTC uses this register for restart continuity"); - hal::Machine::get_rtc_backup_register(index) + assert!(index != 0, "RTC uses this register for restart continuity"); + hal::Machine::rtc_backup_register(index) } pub fn set_rtc_backup_register(index: u8, value: u32) { assert!(index < 32, "RTC backup register index out of bounds"); - assert!(index == 0, "RTC uses this register for restart continuity"); + assert!(index != 0, "RTC uses this register for restart continuity"); hal::Machine::set_rtc_backup_register(index, value) } -pub fn get_actual_time() -> u64 { - let raw = sync::atomic::irq_free(|| hal::Machine::get_rtc_raw()); +pub fn walltime() -> u64 { + let raw = hal::Machine::rtc_raw(); + if raw == -1i64 as u64 { + kprintln!("failed to read RTC time"); + return 0; + } + if raw == -2i64 as u64 { + kprintln!("failed to read RTC date"); + return 0; + } rtc_raw_to_unix(raw) } -pub fn set_actual_time(time: u64) { +pub fn set_walltime(time: u64) { let raw = unix_to_rtc_raw(time); - sync::atomic::irq_free(|| hal::Machine::set_rtc_raw(raw)) + match hal::Machine::set_rtc_raw(raw) { + 0 => (), + -1 => kprintln!("failed to set RTC time"), + -2 => kprintln!("failed to set RTC date"), + _ => kprintln!("unknown error setting RTC time"), + } } const fn bcd_to_bin(value: u8) -> u8 { @@ -98,7 +110,6 @@ fn rtc_raw_to_unix(raw: u64) -> u64 { let hours = bcd_to_bin((raw & 0xff) as u8) as u64; let minutes = bcd_to_bin(((raw >> 8) & 0xff) as u8) as u64; let seconds = bcd_to_bin(((raw >> 16) & 0xff) as u8) as u64; - let weekday = ((raw >> 32) & 0xff) as u8; let year = 2000 + u32::from(bcd_to_bin(((raw >> 56) & 0xff) as u8)); let month = u32::from(bcd_to_bin(((raw >> 40) & 0xff) as u8)); let day = u32::from(bcd_to_bin(((raw >> 48) & 0xff) as u8)); From a82000d7493a68d8cbd75a0e324da454fa5cc582 Mon Sep 17 00:00:00 2001 From: Gabriel Kuznik Date: Sun, 17 May 2026 23:33:33 +0200 Subject: [PATCH 4/5] move rtc into driver use backup register 31 instead of 1 --- machine/api/src/lib.rs | 5 +- machine/cortex-m/src/native.rs | 4 + machine/cortex-m/src/stub.rs | 4 + machine/cortex-m/st/stm32l4/interface/clock.c | 7 +- .../cortex-m/st/stm32l4/interface/export.h | 1 + src/drivers.rs | 2 + src/drivers/clock.rs | 143 ++++++++++++++++++ src/time.rs | 124 +-------------- src/uapi/time.rs | 12 +- 9 files changed, 177 insertions(+), 125 deletions(-) create mode 100644 src/drivers/clock.rs diff --git a/machine/api/src/lib.rs b/machine/api/src/lib.rs index 7749d3f..af0a75a 100644 --- a/machine/api/src/lib.rs +++ b/machine/api/src/lib.rs @@ -25,9 +25,10 @@ pub trait Machinelike { fn monotonic_freq() -> u64; fn rtc_raw() -> u64; fn set_rtc_raw(time: u64) -> i32; - // index 0..32, 0 is used by the RTC + fn init_rtc() -> i32; + // index 0..32, 31 is used by the RTC fn rtc_backup_register(index: u8) -> u32; - // index 0..32, 0 is used by the RTC + // index 0..32, 31 is used by the RTC fn set_rtc_backup_register(index: u8, value: u32); // Returns the frequency of the machine's systick timer in Hz. fn systick_freq() -> u64; diff --git a/machine/cortex-m/src/native.rs b/machine/cortex-m/src/native.rs index ebf49e7..c1f1886 100644 --- a/machine/cortex-m/src/native.rs +++ b/machine/cortex-m/src/native.rs @@ -95,6 +95,10 @@ impl hal_api::Machinelike for ArmMachine { unsafe { bindings::set_rtc_raw(time) } } + fn init_rtc() -> i32 { + unsafe { bindings::init_rtc() } + } + fn rtc_backup_register(index: u8) -> u32 { unsafe { bindings::rtc_backup_register(index) } } diff --git a/machine/cortex-m/src/stub.rs b/machine/cortex-m/src/stub.rs index 6dbdbd5..409962e 100644 --- a/machine/cortex-m/src/stub.rs +++ b/machine/cortex-m/src/stub.rs @@ -50,6 +50,10 @@ impl hal_api::Machinelike for StubMachine { 0 } + fn init_rtc() -> i32 { + 0 + } + fn rtc_backup_register(index: u8) -> u32 { 0 } diff --git a/machine/cortex-m/st/stm32l4/interface/clock.c b/machine/cortex-m/st/stm32l4/interface/clock.c index b837956..915c54e 100644 --- a/machine/cortex-m/st/stm32l4/interface/clock.c +++ b/machine/cortex-m/st/stm32l4/interface/clock.c @@ -89,7 +89,7 @@ int init_rtc(void) return ERROR_RTC_INIT; } - if (HAL_RTCEx_BKUPRead(&rtc_handle, RTC_BKP_DR0) != RTC_BKP_MAGIC) { + if (HAL_RTCEx_BKUPRead(&rtc_handle, RTC_BKP_DR31) != RTC_BKP_MAGIC) { // Sat 01.01.2000 unsigned long long time = ((uint64_t)0) | ((uint64_t)0 << 8U) | @@ -199,7 +199,7 @@ int init_clock_cfg(void) SystemCoreClockUpdate(); init_monotonic_timer(); - return init_rtc(); + return 0; } unsigned long long monotonic_now(void) @@ -272,7 +272,6 @@ long get_rtc_backup_register(unsigned index) void set_rtc_backup_register(unsigned index, long value) { - assert(index != 0 && "Register 0 is reserved for RTC init"); HAL_RTCEx_BKUPWrite(&rtc_handle, RTC_BKP_DR0 + index, value); } @@ -321,6 +320,6 @@ int set_rtc_raw(unsigned long long raw) return -2; } - HAL_RTCEx_BKUPWrite(&rtc_handle, RTC_BKP_DR0, RTC_BKP_MAGIC); + HAL_RTCEx_BKUPWrite(&rtc_handle, RTC_BKP_DR31, RTC_BKP_MAGIC); return 0; } \ No newline at end of file diff --git a/machine/cortex-m/st/stm32l4/interface/export.h b/machine/cortex-m/st/stm32l4/interface/export.h index 8acf23b..81d2e3d 100644 --- a/machine/cortex-m/st/stm32l4/interface/export.h +++ b/machine/cortex-m/st/stm32l4/interface/export.h @@ -229,6 +229,7 @@ unsigned long long monotonic_now(void); unsigned long long monotonic_freq(void); void delay_us(uint32_t delay_us); void do_tick(void); +int init_rtc(void); unsigned long long rtc_raw(void); int set_rtc_raw(unsigned long long time); diff --git a/src/drivers.rs b/src/drivers.rs index fe3e561..d8b3a65 100644 --- a/src/drivers.rs +++ b/src/drivers.rs @@ -1,8 +1,10 @@ pub mod can; +pub mod clock; pub mod i2c; pub mod spi; pub fn init() { + clock::init(); i2c::init(); spi::init(); can::init(); diff --git a/src/drivers/clock.rs b/src/drivers/clock.rs new file mode 100644 index 0000000..3abd348 --- /dev/null +++ b/src/drivers/clock.rs @@ -0,0 +1,143 @@ +use hal_api::PosixError; + +use crate::hal; +use crate::hal::Machinelike; + +/// The monotonic clock is brought up by [hal::Machine::init()] +pub fn init() { + match hal::Machine::init_rtc() { + 0 => (), + -4 => { + kprintln!("failed to initialize RTC: init clock source"); + } + -5 => { + kprintln!("failed to initialize RTC: init RTC"); + } + ret => { + kprintln!("failed to initialize RTC: {ret}"); + } + } +} + +pub fn rtc_backup_register(index: u8) -> u32 { + assert!(index < 32, "RTC backup register index out of bounds"); + assert!(index != 31, "RTC uses this register for restart continuity"); + hal::Machine::rtc_backup_register(index) +} + +pub fn set_rtc_backup_register(index: u8, value: u32) { + assert!(index < 32, "RTC backup register index out of bounds"); + assert!(index != 31, "RTC uses this register for restart continuity"); + hal::Machine::set_rtc_backup_register(index, value) +} + +pub fn walltime() -> Result { + let raw = hal::Machine::rtc_raw(); + if raw == -1i64 as u64 { + kprintln!("failed to read RTC time"); + return Err(PosixError::EIO); + } + if raw == -2i64 as u64 { + kprintln!("failed to read RTC date"); + return Err(PosixError::EIO); + } + Ok(rtc_raw_to_unix(raw)) +} + +pub fn set_walltime(time: u64) -> Result<(), PosixError> { + let raw = unix_to_rtc_raw(time); + match hal::Machine::set_rtc_raw(raw) { + 0 => Ok(()), + -1 => { + kprintln!("failed to set RTC time"); + return Err(PosixError::EINVAL); + } + -2 => { + kprintln!("failed to set RTC date"); + return Err(PosixError::EINVAL); + } + _ => { + kprintln!("unknown error setting RTC time"); + return Err(PosixError::Unknown); + } + } +} + +const fn bcd_to_bin(value: u8) -> u8 { + ((value >> 4) * 10) + (value & 0x0f) +} + +const fn bin_to_bcd(value: u8) -> u8 { + ((value / 10) << 4) | (value % 10) +} + +const fn is_leap_year(year: u32) -> bool { + year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) +} + +const fn days_in_month(year: u32, month: u32) -> u32 { + match month { + 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31, + 4 | 6 | 9 | 11 => 30, + 2 if is_leap_year(year) => 29, + 2 => 28, + _ => 0, + } +} + +fn days_since_unix_epoch(year: u32, month: u32, day: u32) -> u64 { + let mut days = 0u64; + for y in 1970..year { + days += if is_leap_year(y) { 366 } else { 365 }; + } + for m in 1..month { + days += days_in_month(year, m) as u64; + } + days + u64::from(day.saturating_sub(1)) +} + +fn rtc_raw_to_unix(raw: u64) -> u64 { + let hours = bcd_to_bin((raw & 0xff) as u8) as u64; + let minutes = bcd_to_bin(((raw >> 8) & 0xff) as u8) as u64; + let seconds = bcd_to_bin(((raw >> 16) & 0xff) as u8) as u64; + let year = 2000 + u32::from(bcd_to_bin(((raw >> 56) & 0xff) as u8)); + let month = u32::from(bcd_to_bin(((raw >> 40) & 0xff) as u8)); + let day = u32::from(bcd_to_bin(((raw >> 48) & 0xff) as u8)); + + days_since_unix_epoch(year, month, day) * 86_400 + hours * 3_600 + minutes * 60 + seconds +} + +fn unix_to_rtc_raw(unix: u64) -> u64 { + let epoch_2000 = 946_684_800u64; + let mut seconds = unix.saturating_sub(epoch_2000); + let mut days = seconds / 86_400; + seconds %= 86_400; + + let mut year = 2000u32; + while days >= if is_leap_year(year) { 366 } else { 365 } { + days -= if is_leap_year(year) { 366 } else { 365 }; + year += 1; + } + + let mut month = 1u32; + while days >= u64::from(days_in_month(year, month)) { + days -= u64::from(days_in_month(year, month)); + month += 1; + } + + let day = (days + 1) as u32; + let weekday = (((days_since_unix_epoch(year, month, day) + 4) % 7) + 1) as u8; + + let hours = (seconds / 3_600) as u8; + seconds %= 3_600; + let minutes = (seconds / 60) as u8; + let seconds = (seconds % 60) as u8; + + (u64::from(bin_to_bcd(hours))) + | (u64::from(bin_to_bcd(minutes)) << 8) + | (u64::from(bin_to_bcd(seconds)) << 16) + | (u64::from(weekday) << 32) + | (u64::from(bin_to_bcd(month as u8)) << 40) + | (u64::from(bin_to_bcd(day as u8)) << 48) + | (u64::from(bin_to_bcd((year - 2000) as u8)) << 56) +} diff --git a/src/time.rs b/src/time.rs index 01396cf..d3d58fa 100644 --- a/src/time.rs +++ b/src/time.rs @@ -1,28 +1,30 @@ +use crate::drivers::clock; use crate::hal::{self, Machinelike}; use crate::{sched, sync}; static TICKS: sync::atomic::AtomicU64 = sync::atomic::AtomicU64::new(0); -extern "C" fn update_time() { +extern "C" fn update_time(_ctx: *mut core::ffi::c_void) { let interval: u64 = 100_000; // ~100 seconds in ticks kprintln!( - "Time thread started with tick interval {} at {}", + "Time thread started with tick interval {} at {:?}", interval, - walltime() + clock::walltime() ); loop { let tick = tick(); sched::with(|sched| { let _ = sched.sleep_until(tick + interval, tick); }); - kprintln!("time is now {}", walltime()); + kprintln!("time is now {:?}", clock::walltime()); } } pub fn init() { let attrs = sched::thread::Attributes { entry: update_time, + ctx: core::ptr::null_mut(), fin: None, attrs: None, }; @@ -38,120 +40,6 @@ pub fn init() { }) } -pub fn rtc_backup_register(index: u8) -> u32 { - assert!(index < 32, "RTC backup register index out of bounds"); - assert!(index != 0, "RTC uses this register for restart continuity"); - hal::Machine::rtc_backup_register(index) -} - -pub fn set_rtc_backup_register(index: u8, value: u32) { - assert!(index < 32, "RTC backup register index out of bounds"); - assert!(index != 0, "RTC uses this register for restart continuity"); - hal::Machine::set_rtc_backup_register(index, value) -} - -pub fn walltime() -> u64 { - let raw = hal::Machine::rtc_raw(); - if raw == -1i64 as u64 { - kprintln!("failed to read RTC time"); - return 0; - } - if raw == -2i64 as u64 { - kprintln!("failed to read RTC date"); - return 0; - } - rtc_raw_to_unix(raw) -} - -pub fn set_walltime(time: u64) { - let raw = unix_to_rtc_raw(time); - match hal::Machine::set_rtc_raw(raw) { - 0 => (), - -1 => kprintln!("failed to set RTC time"), - -2 => kprintln!("failed to set RTC date"), - _ => kprintln!("unknown error setting RTC time"), - } -} - -const fn bcd_to_bin(value: u8) -> u8 { - ((value >> 4) * 10) + (value & 0x0f) -} - -const fn bin_to_bcd(value: u8) -> u8 { - ((value / 10) << 4) | (value % 10) -} - -const fn is_leap_year(year: u32) -> bool { - year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) -} - -const fn days_in_month(year: u32, month: u32) -> u32 { - match month { - 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31, - 4 | 6 | 9 | 11 => 30, - 2 if is_leap_year(year) => 29, - 2 => 28, - _ => 0, - } -} - -fn days_since_unix_epoch(year: u32, month: u32, day: u32) -> u64 { - let mut days = 0u64; - for y in 1970..year { - days += if is_leap_year(y) { 366 } else { 365 }; - } - for m in 1..month { - days += days_in_month(year, m) as u64; - } - days + u64::from(day.saturating_sub(1)) -} - -fn rtc_raw_to_unix(raw: u64) -> u64 { - let hours = bcd_to_bin((raw & 0xff) as u8) as u64; - let minutes = bcd_to_bin(((raw >> 8) & 0xff) as u8) as u64; - let seconds = bcd_to_bin(((raw >> 16) & 0xff) as u8) as u64; - let year = 2000 + u32::from(bcd_to_bin(((raw >> 56) & 0xff) as u8)); - let month = u32::from(bcd_to_bin(((raw >> 40) & 0xff) as u8)); - let day = u32::from(bcd_to_bin(((raw >> 48) & 0xff) as u8)); - - days_since_unix_epoch(year, month, day) * 86_400 + hours * 3_600 + minutes * 60 + seconds -} - -fn unix_to_rtc_raw(unix: u64) -> u64 { - let epoch_2000 = 946_684_800u64; - let mut seconds = unix.saturating_sub(epoch_2000); - let mut days = seconds / 86_400; - seconds %= 86_400; - - let mut year = 2000u32; - while days >= if is_leap_year(year) { 366 } else { 365 } { - days -= if is_leap_year(year) { 366 } else { 365 }; - year += 1; - } - - let mut month = 1u32; - while days >= u64::from(days_in_month(year, month)) { - days -= u64::from(days_in_month(year, month)); - month += 1; - } - - let day = (days + 1) as u32; - let weekday = (((days_since_unix_epoch(year, month, day) + 4) % 7) + 1) as u8; - - let hours = (seconds / 3_600) as u8; - seconds %= 3_600; - let minutes = (seconds / 60) as u8; - let seconds = (seconds % 60) as u8; - - (u64::from(bin_to_bcd(hours))) - | (u64::from(bin_to_bcd(minutes)) << 8) - | (u64::from(bin_to_bcd(seconds)) << 16) - | (u64::from(weekday) << 32) - | (u64::from(bin_to_bcd(month as u8)) << 40) - | (u64::from(bin_to_bcd(day as u8)) << 48) - | (u64::from(bin_to_bcd((year - 2000) as u8)) << 56) -} - pub fn tick() -> u64 { TICKS.load(sync::atomic::Ordering::Acquire) } diff --git a/src/uapi/time.rs b/src/uapi/time.rs index c05f72f..c1b39dd 100644 --- a/src/uapi/time.rs +++ b/src/uapi/time.rs @@ -1,4 +1,6 @@ -use crate::time; +use hal_api::PosixError; + +use crate::{drivers::clock, time}; pub fn mono_now() -> u64 { time::mono_now() @@ -11,3 +13,11 @@ pub fn mono_freq() -> u64 { pub fn tick() -> u64 { time::tick() } + +pub fn walltime() -> Result { + clock::walltime() +} + +pub fn set_walltime(time: u64) -> Result<(), PosixError> { + clock::set_walltime(time) +} From ef9d0086d3c3b50b65c31af3e4aa8710b07daa8f Mon Sep 17 00:00:00 2001 From: Gabriel Kuznik Date: Thu, 21 May 2026 15:42:55 +0200 Subject: [PATCH 5/5] hal CSS part for LSE --- machine/cortex-m/st/stm32l4/interface/clock.c | 183 +++++++++++------- 1 file changed, 116 insertions(+), 67 deletions(-) diff --git a/machine/cortex-m/st/stm32l4/interface/clock.c b/machine/cortex-m/st/stm32l4/interface/clock.c index 915c54e..ef6f8b2 100644 --- a/machine/cortex-m/st/stm32l4/interface/clock.c +++ b/machine/cortex-m/st/stm32l4/interface/clock.c @@ -3,77 +3,116 @@ #include #include #include "stm32l4xx_hal_rcc.h" +#include "stm32l4xx_hal_rcc_ex.h" #include static volatile uint64_t monotonic_hi = 0; static volatile uint32_t tick = 0; #define RTC_BKP_MAGIC 0x4F534952U -#define LSE_READY_TIMEOUT_LOOPS 2000000U -#define LSI_READY_TIMEOUT_LOOPS 200000U -#define ERROR_CONTROL_VOLTAGE_SCALING -1 -#define ERROR_RCC_OSC_CONFIG -2 -#define ERROR_RCC_CLOCK_CONFIG -3 -#define ERROR_RTC_INIT_CLOCK_SOURCE -4 -#define ERROR_RTC_INIT -5 +// use msb for the error type +// lower byte(s) contain hal status +enum ErrorTypes : uint64_t { + ERROR_CONTROL_VOLTAGE_SCALING = 0x01U << 56U, + ERROR_RCC_OSC_CONFIG = 0x02U << 56U, + ERROR_RCC_CLOCK_CONFIG = 0x03U << 56U, + ERROR_RTC_INIT_CLOCK_SOURCE = 0x04U << 56U, + ERROR_RTC_INIT = 0x05U << 56U, + ERROR_RTC_GET_TIME = 0x06U << 56U, + ERROR_RTC_GET_DATE = 0x07U << 56U, + ERROR_RTC_SET_TIME = 0x08U << 56U, + ERROR_RTC_SET_DATE = 0x09U << 56U +}; static RTC_HandleTypeDef rtc_handle; -static int wait_rcc_ready_flag(uint32_t flag, uint32_t timeout_loops) +/** +* Try to use LSE, fall back to LSI and enable CSS if both are available. +* @retval HAL_StatusTypeDef codes: +* bit 0-1: selecting LSE clock source +* bit 2-3: selecting LSI clock source +* bit 4-5: HAL_TIMEOUT from waiting for LSI ready + */ +static int init_rtc_clock_source(void) { - while (timeout_loops > 0U) { - if (__HAL_RCC_GET_FLAG(flag) != RESET) { - return -1; - } + HAL_PWR_EnableBkUpAccess(); + int error = 0; - timeout_loops--; - } + __HAL_RCC_LSI_ENABLE(); - return 0; -} + __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_HIGH); + __HAL_RCC_LSE_CONFIG(RCC_LSE_ON); -static HAL_StatusTypeDef select_rtc_clock_source(uint32_t source) -{ RCC_PeriphCLKInitTypeDef periph = {0}; - periph.PeriphClockSelection = RCC_PERIPHCLK_RTC; - periph.RTCClockSelection = source; - return HAL_RCCEx_PeriphCLKConfig(&periph); + periph.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; + HAL_StatusTypeDef status = HAL_RCCEx_PeriphCLKConfig(&periph); + + if (status != HAL_OK) { + error = status; + // fallback to LSI + periph.RTCClockSelection = RCC_RTCCLKSOURCE_LSI; + status = HAL_RCCEx_PeriphCLKConfig(&periph); + // if LSI selection also fails, return both errors + if (status != HAL_OK) { + error |= status << 2; + return error; + } + } + + // ensure LSI is ready + uint32_t tickstart = HAL_GetTick(); + while (__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY) == RESET) { + if ((HAL_GetTick() - tickstart) > RCC_LSE_TIMEOUT_VALUE) { + error |= HAL_TIMEOUT << 4; + break; + } + } + + __HAL_RCC_RTC_ENABLE(); + + // clock security system requires both LSE and LSI to be enabled. + if (!error) { + HAL_RCCEx_EnableLSECSS(); + __HAL_RCC_ENABLE_IT(RCC_IT_LSECSS); + } + HAL_PWR_DisableBkUpAccess(); + + return error; } -static int init_rtc_clock_source(void) +void handle_css_lse_interrupt() { - __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_HIGH); - __HAL_RCC_LSE_CONFIG(RCC_LSE_ON); + HAL_PWR_EnableBkUpAccess(); + __HAL_RCC_CLEAR_IT(RCC_IT_LSECSS); - if (!wait_rcc_ready_flag(RCC_FLAG_LSERDY, LSE_READY_TIMEOUT_LOOPS) && - select_rtc_clock_source(RCC_RTCCLKSOURCE_LSE) == HAL_OK) { - __HAL_RCC_RTC_ENABLE(); - return 0; - } - + // The software MUST then disable the LSECSSON bit + HAL_RCCEx_DisableLSECSS(); + // stop the defective 32 kHz oscillator (disabling LSEON) __HAL_RCC_LSE_CONFIG(RCC_LSE_OFF); - __HAL_RCC_LSI_ENABLE(); - if (!wait_rcc_ready_flag(RCC_FLAG_LSIRDY, LSI_READY_TIMEOUT_LOOPS) && - select_rtc_clock_source(RCC_RTCCLKSOURCE_LSI) == HAL_OK) { - __HAL_RCC_RTC_ENABLE(); - return 0; + // and change the RTC clock source (no clock or LSI or HSE, with RTCSEL) + RCC_PeriphCLKInitTypeDef periph = {0}; + periph.PeriphClockSelection = RCC_PERIPHCLK_RTC; + periph.RTCClockSelection = RCC_RTCCLKSOURCE_LSI; + HAL_StatusTypeDef status = HAL_RCCEx_PeriphCLKConfig(&periph); + if (status != HAL_OK) { + // internal clock failed, try again later? + __HAL_RCC_RTC_DISABLE(); } - return -1; + HAL_PWR_DisableBkUpAccess(); } -int set_rtc_raw(unsigned long long raw); -int init_rtc(void) +uint64_t set_rtc_raw(uint64_t raw); +uint64_t init_rtc(void) { __HAL_RCC_PWR_CLK_ENABLE(); - HAL_PWR_EnableBkUpAccess(); - // TODO: setup Clock Security System - if (init_rtc_clock_source()) { - return ERROR_RTC_INIT_CLOCK_SOURCE; + int ret = init_rtc_clock_source(); + if (ret) { + return ERROR_RTC_INIT_CLOCK_SOURCE | ret; } rtc_handle.Instance = RTC; @@ -85,8 +124,9 @@ int init_rtc(void) rtc_handle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH; rtc_handle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN; - if (HAL_RTC_Init(&rtc_handle) != HAL_OK) { - return ERROR_RTC_INIT; + ret = HAL_RTC_Init(&rtc_handle); + if (ret != HAL_OK) { + return ERROR_RTC_INIT | ret; } if (HAL_RTCEx_BKUPRead(&rtc_handle, RTC_BKP_DR31) != RTC_BKP_MAGIC) { @@ -153,7 +193,7 @@ void tim2_hndlr(void) } } -int init_clock_cfg(void) +uint64_t init_clock_cfg(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; @@ -161,8 +201,9 @@ int init_clock_cfg(void) /* 80 MHz on STM32L4+ => Range 1 normal mode, not boost */ __HAL_RCC_PWR_CLK_ENABLE(); - if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK) { - return ERROR_CONTROL_VOLTAGE_SCALING; + int ret = HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); + if (ret != HAL_OK) { + return ERROR_CONTROL_VOLTAGE_SCALING | ret; } /* HSI16 -> PLL -> 80 MHz SYSCLK */ @@ -178,8 +219,9 @@ int init_clock_cfg(void) RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // arbitrary unless you use PLLP RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // arbitrary unless you use PLLQ - if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { - return ERROR_RCC_OSC_CONFIG; + ret = HAL_RCC_OscConfig(&RCC_OscInitStruct); + if (ret != HAL_OK) { + return ERROR_RCC_OSC_CONFIG | ret; } RCC_ClkInitStruct.ClockType = @@ -193,8 +235,9 @@ int init_clock_cfg(void) RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; - if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) { - return ERROR_RCC_CLOCK_CONFIG; + ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4); + if (ret != HAL_OK) { + return ERROR_RCC_CLOCK_CONFIG | ret; } SystemCoreClockUpdate(); @@ -265,39 +308,43 @@ void do_tick(void) } -long get_rtc_backup_register(unsigned index) +uint32_t get_rtc_backup_register(uint32_t index) { return HAL_RTCEx_BKUPRead(&rtc_handle, RTC_BKP_DR0 + index); } -void set_rtc_backup_register(unsigned index, long value) +void set_rtc_backup_register(uint32_t index, uint32_t value) { + HAL_PWR_EnableBkUpAccess(); HAL_RTCEx_BKUPWrite(&rtc_handle, RTC_BKP_DR0 + index, value); + HAL_PWR_DisableBkUpAccess(); } -unsigned long long rtc_raw(void) +uint64_t rtc_raw(void) { RTC_TimeTypeDef time = {0}; RTC_DateTypeDef date = {0}; - if (HAL_RTC_GetTime(&rtc_handle, &time, RTC_FORMAT_BCD) != HAL_OK) { - return -1U; + int ret = HAL_RTC_GetTime(&rtc_handle, &time, RTC_FORMAT_BCD); + if (ret != HAL_OK) { + return ERROR_RTC_GET_TIME | ret; } - if (HAL_RTC_GetDate(&rtc_handle, &date, RTC_FORMAT_BCD) != HAL_OK) { - return -2U; + ret = HAL_RTC_GetDate(&rtc_handle, &date, RTC_FORMAT_BCD); + if (ret != HAL_OK) { + return ERROR_RTC_GET_DATE | ret; } return ((uint64_t)time.Hours) | ((uint64_t)time.Minutes << 8U) | ((uint64_t)time.Seconds << 16U) | - ((uint64_t)date.WeekDay << 32U) | - ((uint64_t)date.Month << 40U) | - ((uint64_t)date.Date << 48U) | - ((uint64_t)date.Year << 56U); + ((uint64_t)date.WeekDay << 24U) | + ((uint64_t)date.Month << 32U) | + ((uint64_t)date.Date << 40U) | + ((uint64_t)date.Year << 48U); } -int set_rtc_raw(unsigned long long raw) +uint64_t set_rtc_raw(uint64_t raw) { RTC_TimeTypeDef rtc_time = {0}; RTC_DateTypeDef rtc_date = {0}; @@ -312,12 +359,14 @@ int set_rtc_raw(unsigned long long raw) rtc_date.Date = (uint8_t)((raw >> 48U) & 0xFFU); rtc_date.Year = (uint8_t)((raw >> 56U) & 0xFFU); - if (HAL_RTC_SetTime(&rtc_handle, &rtc_time, RTC_FORMAT_BCD) != HAL_OK) { - return -1; + int ret = HAL_RTC_SetTime(&rtc_handle, &rtc_time, RTC_FORMAT_BCD); + if (ret != HAL_OK) { + return ERROR_RTC_SET_TIME | ret; } - if (HAL_RTC_SetDate(&rtc_handle, &rtc_date, RTC_FORMAT_BCD) != HAL_OK) { - return -2; + ret = HAL_RTC_SetDate(&rtc_handle, &rtc_date, RTC_FORMAT_BCD); + if (ret != HAL_OK) { + return ERROR_RTC_SET_DATE | ret; } HAL_RTCEx_BKUPWrite(&rtc_handle, RTC_BKP_DR31, RTC_BKP_MAGIC);