--- /dev/null
+// ARMware - an ARM emulator
+// Copyright (C) <2007> Wei Hu <wei.hu.tw@gmail.com>
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#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
--- /dev/null
+// ARMware - an ARM emulator
+// Copyright (C) <2007> Wei Hu <wei.hu.tw@gmail.com>
+//
+// 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 <http://www.gnu.org/licenses/>.
+//
+
+#ifndef OS_TIMER_H
+#define OS_TIMER_H
+
+#include "logger.h"
+
+#include <cstdint>
+
+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