feat: Test RTC interrupt

This commit is contained in:
ParrotXray 2025-10-16 20:34:20 +08:00
parent fa311cfab4
commit 74de8650fe
5 changed files with 127 additions and 25 deletions

View File

@ -22,6 +22,9 @@
// - 用戶態/內核態切換
// BUG
// - 在測試RTC中斷時 rtc::get_time() 處卡住
// ==================== 高半核地址空間佈局 ====================
//
// 0x0000_0000_0000 ┌─────────────────────┐

View File

@ -43,9 +43,12 @@ lazy_static! {
// IRQ 1 (33) - Keyboard
idt[33].set_handler_fn(keyboard_interrupt_handler); // IRQ 1 Keyboard
// IRQ 2-15 (34-47) - default
idt[40].set_handler_fn(rtc_interrupt_handler); // IRQ 8
for i in 34..=47 {
idt[i].set_handler_fn(default_irq_handler);
if i != 40 { // 跳過 RTC
idt[i].set_handler_fn(default_irq_handler);
}
}
idt
};

View File

@ -262,4 +262,14 @@ pub extern "x86-interrupt" fn keyboard_interrupt_handler(stack_frame: InterruptS
pub extern "x86-interrupt" fn default_irq_handler(stack_frame: InterruptStackFrame) {
lapic::send_eoi();
log_trace!("Unhandled IRQ: {:#?}", stack_frame);
}
// kernel/src/arch/amd64/isr.rs
/// RTC 中斷處理 (IRQ 8, Vector 40)
pub extern "x86-interrupt" fn rtc_interrupt_handler(_stack_frame: InterruptStackFrame) {
// 必須讀取 Register C 來清除 RTC 中斷標誌
crate::hal::rtc::handle_interrupt();
// 發送 EOI
crate::hal::lapic::send_eoi();
}

View File

@ -120,7 +120,7 @@ impl Rtc {
}
}
/// 將 BCD 轉換為二進制
/// Convert BCD to binary
fn bcd_to_binary(bcd: u8) -> u8 {
(bcd & 0x0F) + ((bcd >> 4) * 10)
}
@ -133,17 +133,18 @@ impl Rtc {
pub fn init(&mut self) {
unsafe {
// Read status register B
let status_b = Self::read_register(RTC_REG_B);
let status_b = Self::read_register(RTC_REG_B | WITH_NMI_DISABLED);
self.binary_mode = (status_b & RTC_BIN_ENCODED_BIT) != 0;
self.hour_24_mode = (status_b & RTC_24HRS_ENCODED_BIT) != 0;
crate::log_debug!(
"RTC Mode: {} encoding, {} hour format",
if self.binary_mode { "Binary" } else { "BCD" },
if self.hour_24_mode { "24" } else { "12" }
);
let mut reg_a = Self::read_register(RTC_REG_A | WITH_NMI_DISABLED);
reg_a = (reg_a & 0xF0) | RTC_DIVIDER_33KHZ | RTC_FREQUENCY_1024HZ;
Self::write_register(RTC_REG_A | WITH_NMI_DISABLED, reg_a);
// ⭐ CRITICAL: Read Register C to clear any pending interrupts!
Self::read_register(RTC_REG_C);
self.disable_timer();
}
}
@ -162,7 +163,7 @@ impl Rtc {
(second, minute, hour, day, month, year, weekday)
}
/// Convert the value
/// Convert the value based on encoding mode
fn convert_value(&self, value: u8) -> u8 {
if self.binary_mode {
value
@ -177,12 +178,14 @@ impl Rtc {
let (mut second, mut minute, mut hour, mut day, mut month, mut year, weekday) =
self.read_raw();
// Convert from BCD to binary when needed
second = self.convert_value(second);
minute = self.convert_value(minute);
day = self.convert_value(day);
month = self.convert_value(month);
year = self.convert_value(year);
// Handle 12-hour format
let pm_bit = hour & 0x80;
hour = self.convert_value(hour & 0x7F);
@ -222,15 +225,9 @@ impl Rtc {
/// Enable RTC timer interrupt (1024Hz)
pub fn enable_timer(&self) {
unsafe {
// Disable NMI and set frequency
let mut reg_a = Self::read_register(RTC_REG_A);
reg_a = (reg_a & 0xF0) | RTC_DIVIDER_33KHZ | RTC_FREQUENCY_1024HZ;
Self::write_register(RTC_REG_A, reg_a);
// Enable periodic interrupts
let mut reg_b = Self::read_register(RTC_REG_B);
let mut reg_b = Self::read_register(RTC_REG_B | WITH_NMI_DISABLED);
reg_b |= RTC_TIMER_ON;
Self::write_register(RTC_REG_B, reg_b);
Self::write_register(RTC_REG_B | WITH_NMI_DISABLED, reg_b);
log_info!("RTC timer enabled at {}Hz", RTC_TIMER_BASE_FREQUENCY);
}
@ -239,9 +236,9 @@ impl Rtc {
/// Disable RTC timer interrupt
pub fn disable_timer(&self) {
unsafe {
let mut reg_b = Self::read_register(RTC_REG_B);
let mut reg_b = Self::read_register(RTC_REG_B | WITH_NMI_DISABLED);
reg_b &= !RTC_TIMER_ON;
Self::write_register(RTC_REG_B, reg_b);
Self::write_register(RTC_REG_B | WITH_NMI_DISABLED, reg_b);
log_info!("RTC timer disabled");
}
@ -322,12 +319,30 @@ pub fn handle_interrupt() {
/// RTC periodic interrupt callback (can be overwritten by other modules)
#[allow(dead_code)]
fn on_periodic_interrupt() {
// 在這裡處理定時器事件
// 例如: 更新系統時間、調度任務等
// Handle timer events here
// e.g., update system time, schedule tasks, etc.
// For testing: increment counter
unsafe {
RTC_TICK_COUNT += 1;
}
}
/// RTC alarm interrupt callback
#[allow(dead_code)]
fn on_alarm_interrupt() {
// 在這裡處理鬧鐘事件
// Handle alarm events here
}
// Test counter
static mut RTC_TICK_COUNT: u64 = 0;
/// Get RTC tick count (for testing)
pub fn get_tick_count() -> u64 {
unsafe { RTC_TICK_COUNT }
}
/// Reset tick count (for testing)
pub fn reset_tick_count() {
unsafe { RTC_TICK_COUNT = 0; }
}

View File

@ -54,12 +54,29 @@ pub fn _kernel_main() -> ! {
// Unmask IRQ 1 (enable keyboard interrupt)
crate::hal::ioapic::unmask_irq(1);
log_info!("Keyboard interrupt unmasked");
log_info!("Setting up RTC interrupt (IRQ 8 -> Vector 40)");
crate::hal::ioapic::set_irq_redirect(
8, // RTC is IRQ 8
40, // Vector 40
apic_id as u8,
false, // Edge triggered
false // Active high
);
crate::hal::ioapic::unmask_irq(8);
log_info!("RTC interrupt unmasked");
log_info!("Enabling RTC timer interrupt (1024Hz)...");
// Reset counter
rtc::reset_tick_count();
rtc::enable_timer();
cpu::cpu_enable_interrupts();
log_info!("CPU interrupts enabled");
test_rtc_interrupt();
kprintln!();
log_info!("Interrupt system ready!");
@ -75,4 +92,58 @@ pub fn _kernel_main() -> ! {
loop {
cpu::cpu_halt();
}
}
fn test_rtc_interrupt() {
log_info!("=== RTC Interrupt Test ===");
// Wait and check tick count
log_info!("Waiting for RTC interrupts...");
let start_count = rtc::get_tick_count();
// Busy wait for ~1 second (approximately)
for _ in 0..1000000 {
cpu::cpu_pause();
}
let end_count = rtc::get_tick_count();
let ticks = end_count - start_count;
if ticks > 0 {
log_info!("RTC interrupt working! Received {} ticks", ticks);
log_info!("Expected: ~1024 ticks/second");
log_info!("Actual rate: {} Hz", ticks);
} else {
log_error!("RTC interrupt NOT working! No ticks received");
}
// Live counter display
log_info!("Live tick counter (press any key to continue):");
let mut last_count = rtc::get_tick_count();
let mut seconds = 0;
for _ in 0..5 { // Display for 5 seconds
// Wait approximately 1 second
for _ in 0..1000000 {
cpu::cpu_pause();
}
let current_count = rtc::get_tick_count();
let delta = current_count - last_count;
last_count = current_count;
seconds += 1;
kprintln!(" [{}s] Total ticks: {}, Delta: {}, Rate: {} Hz",
seconds, current_count, delta, delta);
// Also show current time
// if let Some(time) = rtc::get_time() {
// kprintln!(" Time: {}", time.format());
// }
}
log_info!("RTC test complete!");
kprintln!();
}