From e2ed8639c6eb1be0eefcfee37e58995e066ab6b7 Mon Sep 17 00:00:00 2001 From: George Wright Date: Wed, 24 Jun 2026 19:13:42 -0700 Subject: [PATCH] sa1100: add initial os timer implementation --- CMakeLists.txt | 1 + WindCore/series7/os_timer.cpp | 221 ++++++++++++++++++++++++++++++++++ WindCore/series7/os_timer.h | 101 ++++++++++++++++ 3 files changed, 323 insertions(+) create mode 100644 WindCore/series7/os_timer.cpp create mode 100644 WindCore/series7/os_timer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c81d37f..0289585 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ set(WIND_CORE_SOURCES WindCore/series5mx/windermere.cpp WindCore/series7/gpio_controller.cpp WindCore/series7/lcd_controller.cpp + WindCore/series7/os_timer.cpp WindCore/series7/sa1100.cpp WindCore/series7/sa1100_memory.cpp ) diff --git a/WindCore/series7/os_timer.cpp b/WindCore/series7/os_timer.cpp new file mode 100644 index 0000000..46d067a --- /dev/null +++ b/WindCore/series7/os_timer.cpp @@ -0,0 +1,221 @@ +// ARMware - an ARM emulator +// Copyright (C) <2007> Wei Hu +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "os_timer.h" + +#include "logger.h" + +namespace SA1100 { + +OsTimer::OsTimer() { initRegisters(); } + +void OsTimer::initRegisters() { + for (uint32_t i = 0; i < 4; ++i) { + mOSMR[i] = 0; + } + + mOSCR = 0; + mOSSR = 0; + mOWER = 0; + mOIER = 0; +} + +void OsTimer::reset() { + // :SA-1110 Developer's Manual: Wei 2004-Apr-24: + // + // WME (Watchdog Match Enable) bit is set by writing a one to it. + // It can only be cleared by one of the reset functions (hardware reset, + // software reset) and by entering sleep mode. + // A watchdog reset also clears the watchdog enable bit. + mOWER = 0; + + // :SA-1110 Developer's Manual: p.97: Wei 2004-Apr-24: + // + // the value of bits 0 ~ 3 of OSSR register are unknown at reset. + mOSSR &= 0xF; + + mOIER = 0; +} + +void OsTimer::tick() { + // :SA-1110 Developer's Manual: p.96: Wei 2004-Apr-24: + // + // OSMR[0..3] are compared against the OSCR following every rising + // edge of the 3.6864-MHz clock. + switch (mOIER & 0xF) { + case 0: + break; + + default: + checkTimerStatuses(mOIER & 0xF); + } + + // :SA-1110 Developer's Manual: p.96: Wei 2004-Apr-24: + // + // The OS timer count register is a 32-bit counter that increments + // on rising edges of the 3.6864-MHz clock. + + // :NOTE: Wei 2004-Apr-25: + // + // According to the Linux Kernel source about the setup_timer() routine, + // I think the increment of OSCR register should be put after the + // comparation between OSCR & OSMR. + ++mOSCR; +} + +uint32_t OsTimer::read32(const uint32_t address) const { + switch (address) { + case OSMR0: + LOG_REG_R("OSTimer OSMR0", address, mOSMR[0]); + return mOSMR[0]; + + case OSMR1: + LOG_REG_R("OSTimer OSMR1", address, mOSMR[1]); + return mOSMR[1]; + + case OSMR2: + LOG_REG_R("OSTimer OSMR2", address, mOSMR[2]); + return mOSMR[2]; + + case OSMR3: + LOG_REG_R("OSTimer OSMR3", address, mOSMR[3]); + return mOSMR[3]; + + case OSCR: + // LOG_REG_R("OSTimer OSCR", address, mOSCR); + return mOSCR; + + case OSSR: + LOG_REG_R("OSTimer OSSR", address, mOSSR); + return mOSSR; + + case OWER: + LOG_REG_R("OSTimer OWER", address, mOWER); + return mOWER; + + case OIER: + LOG_REG_R("OSTimer OIER", address, mOIER); + return mOIER; + + default: + LOG_REG_R("OSTimer Unknown", address, 0); + return 0; + } +} + +void OsTimer::write32(const uint32_t address, const uint32_t value) { + switch (address) { + case OSMR0: { + LOG_REG_W("OSTimer OSMR0", address, value); + auto delta = value - mOSCR; + LOG("OSTimer 0 set to %d ticks (%fms)\n", delta, delta / 3686.400f); + mOSMR[0] = value; + break; + } + case OSMR1: { + LOG_REG_W("OSTimer OSMR1", address, value); + auto delta = value - mOSCR; + LOG("OSTimer 1 set to %d ticks (%fms)\n", delta, delta / 3686.400f); + mOSMR[1] = value; + break; + } + case OSMR2: { + LOG_REG_W("OSTimer OSMR2", address, value); + auto delta = value - mOSCR; + LOG("OSTimer 2 set to %d ticks (%fms)\n", delta, delta / 3686.400f); + mOSMR[2] = value; + break; + } + case OSMR3: { + LOG_REG_W("OSTimer OSMR3", address, value); + auto delta = value - mOSCR; + LOG("OSTimer 3 set to %d ticks (%fms)\n", delta, delta / 3686.400f); + mOSMR[3] = value; + break; + } + case OSCR: + LOG_REG_W("OSTimer OSCR", address, value); + mOSCR = value; + break; + + case OSSR: + LOG_REG_W("OSTimer OSSR", address, value); + // :SA-1110 Developer's Manual: p.97: Wei 2004-Apr-24: + // + // bits 0 ~ 3 of OSSR register are cleared by writing a one to the + // proper bit position. Writing zeros to this register has no effect. + // All reserved bits read as zeros and are unaffected by writes; + switch (value & 0xF) { + case 0x0: // 0000 + break; + default: + mOSSR &= ~(value & 0xF); + break; + } + break; + + case OWER: + LOG_REG_W("OSTimer OWER", address, value); + // :SA-1110 Developer's Manual: p.96: Wei 2004-Apr-24: + // + // WME (Watchdog Match Enable) bit is set by writing a one to it. + // It can only be cleared by one of the reset functions (hardware reset, + // software reset) and by entering sleep mode. + // A watchdog reset also clears the watchdog enable bit. + + // TODO: implement watchdog support + break; + + case OIER: + LOG_REG_W("OSTimer OIER", address, value); + // :NOTE: Wei 2004-Apr-24: + // + // According to OSSR register: All reserved bits read as zeros and are + // unaffected by writes I think OIER is the same with OSSR. + mOIER = (value & 0xF); + break; + + default: + LOG_REG_W("OSTimer Unknown", address, value); + break; + } +} + +void OsTimer::advanceTicks(int64_t ticks) { + if (ticks <= 0) + return; + + uint32_t start_OSCR = mOSCR; + uint32_t end_OSCR = start_OSCR + (uint32_t)ticks; + + uint32_t enabled_matches = mOIER & 0xF; + if (enabled_matches) { + for (int i = 0; i < 4; ++i) { + if (enabled_matches & (1 << i)) { + uint32_t diff = mOSMR[i] - start_OSCR; + if (diff < (uint32_t)ticks) { + LOG("OSMR%d triggered via bulk advance\n", i); + mOSSR |= (1 << i); + } + } + } + } + + mOSCR = end_OSCR; +} + +} // namespace SA1100 diff --git a/WindCore/series7/os_timer.h b/WindCore/series7/os_timer.h new file mode 100644 index 0000000..e76def9 --- /dev/null +++ b/WindCore/series7/os_timer.h @@ -0,0 +1,101 @@ +// ARMware - an ARM emulator +// Copyright (C) <2007> Wei Hu +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef OS_TIMER_H +#define OS_TIMER_H + +#include "logger.h" + +#include + +namespace SA1100 { + +class IntController; + +class OsTimer { +public: + OsTimer(); + + void reset(); + void tick(); + void advanceTicks(int64_t ticks); + + inline uint32_t getInterruptStatus() const { return mOSSR; } + + uint32_t read32(const uint32_t address) const; + void write32(const uint32_t address, const uint32_t value); + +private: + enum { + OSMR0 = 0x90000000, + OSMR1 = 0x90000004, + OSMR2 = 0x90000008, + OSMR3 = 0x9000000C, + OSCR = 0x90000010, + OSSR = 0x90000014, + OWER = 0x90000018, + OIER = 0x9000001C + }; + + enum { + OSMR0_MASK = (1 << 0), + OSMR1_MASK = (1 << 1), + OSMR2_MASK = (1 << 2), + OSMR3_MASK = (1 << 3) + }; + + void initRegisters(); + + uint32_t mOSMR[4]; // OS timer match register 0 ~ 3 + uint32_t mOSCR; // OS timer counter register + uint32_t mOSSR; // OS timer status register + uint32_t mOWER; // OS timer watchdog enable register + uint32_t mOIER; // OS timer interrupt enable register + + inline void checkTimerStatuses(uint32_t osmrs) { + if (osmrs & OSMR0_MASK) { + if (mOSCR == mOSMR[0]) { + LOG("OSMR0 triggered\n"); + mOSSR |= OSMR0_MASK; + } + } + if (osmrs & OSMR1_MASK) { + if (mOSCR == mOSMR[1]) { + LOG("OSMR1 triggered\n"); + mOSSR |= OSMR1_MASK; + } + } + if (osmrs & OSMR2_MASK) { + if (mOSCR == mOSMR[2]) { + LOG("OSMR2 triggered\n"); + mOSSR |= OSMR2_MASK; + } + } + if (osmrs & OSMR3_MASK) { + if (mOSCR == mOSMR[3]) { + LOG("OSMR3 triggered\n"); + mOSSR |= OSMR3_MASK; + } + } + } + + friend class IntController; +}; + +} // namespace SA1100 + +#endif // OS_TIMER_H -- 2.45.2