From 7a76c578f9011904a6d6db665113d697001dc26f Mon Sep 17 00:00:00 2001 From: George Wright Date: Tue, 3 Dec 2024 09:18:07 -0800 Subject: [PATCH] initial impl of sa-1100 core and associated peripherals for netbook/s7 emulation --- WindCore/sa1100/SA1100.pro | 5 + WindCore/sa1100/asic14.cpp | 152 +++++++ WindCore/sa1100/asic14.h | 111 +++++ WindCore/sa1100/gpio_controller.cpp | 157 +++++++ WindCore/sa1100/gpio_controller.h | 149 ++++++ WindCore/sa1100/interrupt_controller.cpp | 547 +++++++++++++++++++++++ WindCore/sa1100/interrupt_controller.h | 158 +++++++ WindCore/sa1100/lcd_controller.cpp | 207 +++++++++ WindCore/sa1100/lcd_controller.h | 192 ++++++++ WindCore/sa1100/os_timer.cpp | 181 ++++++++ WindCore/sa1100/os_timer.h | 113 +++++ WindCore/sa1100/power_manager.cpp | 84 ++++ WindCore/sa1100/power_manager.h | 81 ++++ WindCore/sa1100/reset_controller.cpp | 48 ++ WindCore/sa1100/reset_controller.h | 64 +++ WindCore/sa1100/serial_3.h | 54 +++ WindCore/sa1100/uart.cpp | 322 +++++++++++++ WindCore/sa1100/uart.h | 153 +++++++ 18 files changed, 2778 insertions(+) create mode 100644 WindCore/sa1100/SA1100.pro create mode 100644 WindCore/sa1100/asic14.cpp create mode 100644 WindCore/sa1100/asic14.h create mode 100644 WindCore/sa1100/gpio_controller.cpp create mode 100644 WindCore/sa1100/gpio_controller.h create mode 100644 WindCore/sa1100/interrupt_controller.cpp create mode 100644 WindCore/sa1100/interrupt_controller.h create mode 100644 WindCore/sa1100/lcd_controller.cpp create mode 100644 WindCore/sa1100/lcd_controller.h create mode 100644 WindCore/sa1100/os_timer.cpp create mode 100644 WindCore/sa1100/os_timer.h create mode 100644 WindCore/sa1100/power_manager.cpp create mode 100644 WindCore/sa1100/power_manager.h create mode 100644 WindCore/sa1100/reset_controller.cpp create mode 100644 WindCore/sa1100/reset_controller.h create mode 100644 WindCore/sa1100/serial_3.h create mode 100644 WindCore/sa1100/uart.cpp create mode 100644 WindCore/sa1100/uart.h diff --git a/WindCore/sa1100/SA1100.pro b/WindCore/sa1100/SA1100.pro new file mode 100644 index 0000000..4f6782a --- /dev/null +++ b/WindCore/sa1100/SA1100.pro @@ -0,0 +1,5 @@ +SOURCES += \ + memory_conf.cpp + +HEADERS += \ + memory_conf.h diff --git a/WindCore/sa1100/asic14.cpp b/WindCore/sa1100/asic14.cpp new file mode 100644 index 0000000..75ee9e3 --- /dev/null +++ b/WindCore/sa1100/asic14.cpp @@ -0,0 +1,152 @@ +#include "asic14.h" + +namespace SA1100 { + + ASIC14::ASIC14() { + init_register(); + } + + void ASIC14::reset() { + m_CTRL0 = 0; + m_KBD_PADR = 0; + m_CTRL_STATUS = 0; + m_POWER = 0; + m_STATUS = 0; + m_STATUS4 = 0; + m_CONTRAST = 0; + m_BRIGHTNESS = 0; + m_IRQ_STATUS = 0; + m_IRQ_MASK = 0xffff; + m_STATUS3 = 0; + m_KBD_SCAN = 0; + m_IRQ_EDGE = 0; + m_SPI_DATA = 0; + m_SPI_FN = 0; + } + + void ASIC14::run() {} + + uint16_t ASIC14::get_data(uint32_t const address) const { + switch (address) { + case CTRL0: + printf("ASIC14 read register CTRL0: %04x\n", m_CTRL0); + return m_CTRL0; + case KBD_PADR: + printf("ASIC14 read register KBD_PADR: %04x\n", m_KBD_PADR); + return m_KBD_PADR; + case CTRL_STATUS: + printf("ASIC14 read register CTRL_STATUS: %04x\n", m_CTRL_STATUS); + return m_CTRL_STATUS; + case POWER: + printf("ASIC14 read register POWER: %04x\n", m_POWER); + return m_POWER; + case STATUS4: + printf("ASIC14 read register STATUS4: %04x\n", m_STATUS4); + return m_STATUS4; + case CONTRAST: + printf("ASIC14 read register CONTRAST: %04x\n", m_CONTRAST); + return m_CONTRAST; + case BRIGHTNESS: + printf("ASIC14 read register BRIGHTNESS: %04x\n", m_BRIGHTNESS); + return m_BRIGHTNESS; + case IRQ_STATUS: + printf("ASIC14 read register IRQ_STATUS: %04x\n", m_IRQ_STATUS); + return m_IRQ_STATUS; + case IRQ_MASK: + printf("ASIC14 read register IRQ_MASK: %04x\n", m_IRQ_MASK); + return m_IRQ_MASK; + case STATUS3: + printf("ASIC14 read register STATUS3: %04x\n", m_STATUS3); + return m_STATUS3; + case KBD_SCAN: + printf("ASIC14 read register KBD_SCAN: %04x\n", m_KBD_SCAN); + return m_KBD_SCAN; + case IRQ_EDGE: + printf("ASIC14 read register IRQ_EDGE: %04x\n", m_IRQ_EDGE); + return m_IRQ_EDGE; + case STATUS6: + printf("ASIC14 read register STATUS6: %04x\n", m_STATUS6); + return m_STATUS6 | 2; + case SPI_DATA: + printf("ASIC14 read register SPI_DATA: %04x\n", m_SPI_DATA); + return m_SPI_DATA; + case SPI_FN: + printf("ASIC14 read register SPI_FN: %04x\n", m_SPI_FN); + return m_SPI_FN; + default: + printf("Unknown ASIC14 register requested %08x\n", address); + return 0; + } + } + + void ASIC14::put_data(uint32_t const address, uint16_t const value) { + switch (address) { + case CTRL0: + printf("ASIC14 write %04x to register CTRL0\n", value); + m_CTRL0 = value; + break; + case KBD_PADR: + printf("ASIC14 write %04x to register KBD_PADR\n", value); + m_KBD_PADR = value; + break; + case CTRL_STATUS: + printf("ASIC14 write %04x to register CTRL_STATUS\n", value); + m_CTRL_STATUS = value; + break; + case POWER: + printf("ASIC14 write %04x to register POWER\n", value); + m_POWER = value; + break; + case STATUS4: + printf("ASIC14 write %04x to register STATUS4\n", value); + m_STATUS4 &= ~value; + break; + case CONTRAST: + printf("ASIC14 write %04x to register CONTRAST\n", value); + m_CONTRAST = value; + break; + case BRIGHTNESS: + printf("ASIC14 write %04x to register BRIGHTNESS\n", value); + m_BRIGHTNESS = value; + break; + case IRQ_STATUS: + printf("ASIC14 write %04x to register IRQ_STATUS\n", value); + m_IRQ_STATUS = value; + break; + case IRQ_MASK: + printf("ASIC14 write %04x to register IRQ_MASK\n", value); + m_IRQ_MASK = value; + break; + case STATUS3: + printf("ASIC14 write %04x to register STATUS3\n", value); + m_STATUS3 &= ~value; + break; + case KBD_SCAN: + printf("ASIC14 write %04x to register KBD_SCAN\n", value); + m_KBD_SCAN = value; + break; + case IRQ_EDGE: + printf("ASIC14 write %04x to register IRQ_EDGE\n", value); + m_IRQ_EDGE = value; + break; + case STATUS6: + printf("ASIC14 write %04x to register STATUS6\n", value); + // Clear interrupts on STATUS6 according to the bit mask provided + m_STATUS6 &= ~value; + break; + case SPI_DATA: + printf("ASIC14 write %04x to register SPI_DATA\n", value); + m_SPI_DATA = value; + break; + case SPI_FN: + printf("ASIC14 write %04x to register SPI_FN\n", value); + m_SPI_FN = value; + break; + default: + printf("ASIC14 write %04x to unknown register %08x\n", value, address); + break; + } + return; + } + +} // namespace SA1100 diff --git a/WindCore/sa1100/asic14.h b/WindCore/sa1100/asic14.h new file mode 100644 index 0000000..98d7633 --- /dev/null +++ b/WindCore/sa1100/asic14.h @@ -0,0 +1,111 @@ +#ifndef ASIC14_H +#define ASIC14_H + +#include + +#include "gpio_controller.h" +#include "lcd_controller.h" +#include "os_timer.h" +#include "serial_3.h" +#include "uart.h" + +// An implementation of ASIC14 from the Psion netBook. +// This is mapped into the 0x10000000 address space and +// appears to handle power/battery status, CF/PCMCIA power, +// two timers, LCD brightness/contrast and their associated +// interrupts. + +namespace SA1100 { + +class ASIC14 { + public: + enum IRQMaskEnum + { + IRQ_TIMER0 = (1<<0), + IRQ_TIMER1 = (1<<1), + IRQ_Rx = (1<<2), + IRQ_Tx = (1<<3), + IRQ_E2PROM = (1<<4), + IRQ_Rx_OVERRUN = (1<<5), + IRQ_E2PROM_ERR = (1<<6), + IRQ_ADC = (1<<7), + IRQ_PCMCIA = (1<<9), + // Bit 8? + IRQ_CFCARD = (1<<10), + // Bits 11 and 12 are expansion interrupts? + IRQ_CFCARD_CHANGE = (1<<13), + IRQ_PCMCIA_CHANGE = (1<<14), + IRQ_PEN = (1<<15) + }; + typedef enum IRQMaskEnum IRQMaskEnum; + + private: + enum + { + CTRL0 = 0x10000000, + KBD_PADR = 0x10000004, + CTRL_STATUS = 0x10000006, + POWER = 0x10000008, + STATUS = 0x1000000a, + STATUS4 = 0x10000012, + CONTRAST = 0x10000016, + BRIGHTNESS = 0x10000018, + IRQ_STATUS = 0x1000002a, + IRQ_MASK = 0x1000002c, + STATUS3 = 0x1000002e, + KBD_SCAN = 0x10000030, + IRQ_EDGE = 0x10000032, + STATUS6 = 0x10000040, + SPI_DATA = 0x10000048, + SPI_FN = 0x1000004c + }; + + uint16_t m_CTRL0; + uint16_t m_KBD_PADR; + uint16_t m_CTRL_STATUS; + uint16_t m_POWER; + uint16_t m_STATUS; + uint16_t m_STATUS4; + uint16_t m_CONTRAST; + uint16_t m_BRIGHTNESS; + uint16_t m_IRQ_STATUS; + uint16_t m_IRQ_MASK; + uint16_t m_STATUS3; + uint16_t m_KBD_SCAN; + uint16_t m_IRQ_EDGE; + uint16_t m_STATUS6; + uint16_t m_SPI_DATA; + uint16_t m_SPI_FN; + + void init_register() { + m_CTRL0 = 0; + m_KBD_PADR = 0; + m_CTRL_STATUS = 0; + m_POWER = 0; + m_STATUS = 0; + m_STATUS4 = 0; + m_CONTRAST = 0; + m_BRIGHTNESS = 0; + m_IRQ_STATUS = 0; + m_IRQ_MASK = 0; + m_STATUS3 = 0; + m_KBD_SCAN = 0; + m_IRQ_EDGE = 0; + m_SPI_DATA = 0; + m_SPI_FN = 0; + } + + public: + ASIC14(); + + void run(); + void reset(); + + uint16_t get_data(uint32_t const address) const; + + void put_data(uint32_t const address, uint16_t const value); +}; + +} // namespace SA1100 + +#endif // ASIC14_H diff --git a/WindCore/sa1100/gpio_controller.cpp b/WindCore/sa1100/gpio_controller.cpp new file mode 100644 index 0000000..d3c20ba --- /dev/null +++ b/WindCore/sa1100/gpio_controller.cpp @@ -0,0 +1,157 @@ +// 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 "gpio_controller.h" + +namespace SA1100 { + void GPIOController::reset() { + m_GPLR &= 0xFFFFFFF; + m_GEDR &= 0xFFFFFFF; + m_GPDR = 0; + m_GAFR = 0; + + release_action_button(); + + m_GPLR_backup = m_GPLR; + } + + uint32_t GPIOController::get_data(uint32_t const address) const { + switch (address) { + case GPLR: + return m_GPLR; + case GPDR: + return m_GPDR; + case GPSR: + case GPCR: + // :SA-1110 Developer's Manual: p.77: Wei 2004-Jun-05: + // + // GPSR & GPCR are write-only registers. + return 0; + case GRER: + return m_GRER; + case GFER: + return m_GFER; + case GEDR: + return m_GEDR; + case GAFR: + return m_GAFR; + default: + return 0; + } + } + + void GPIOController::put_data(uint32_t const address, uint32_t const value) { + switch (address) { + case GPLR: + // :SA-1110 Developer's Manual: p.75: Wei 2004-Jun-05: + // + // GPLR is a read-only register. + break; + case GPDR: + // :SA-1110 Developer's Manual: p.233: Wei 2004-Jun-06: + // + // In active mode, GPIO pins 2..9 are also used. + // Note that the user must configure GPIO pins 2..9 as outputs (for 16-bit/pixel mode) + // by setting the appropriate bits within the GPIO pin direction register (GPDR) + // and GPIO alternate function register (GAFR). + // + // :NOTE: Wei 2004-Jun-06: + // + // ARMware supports only TFT (active) mode now. + //assert(LDD_BITS == (value & LDD_BITS)); + + // :SA-1110 Developer's Manual: p.76: Wei 2004-Jun-05: + // + // The upper 4 bits are always 0. + m_GPDR = (value & 0xFFFFFFF); + break; + + case GPSR: + { + // Determine what pins are configured as output and we want to set it value now. + uint32_t const temp = (m_GPDR & (value & 0xFFFFFFF)); + uint32_t const new_GPLR = m_GPLR | temp; + uint32_t const diff = m_GPLR ^ new_GPLR; + + // Update GEDR + m_GEDR |= (m_GRER & (new_GPLR & diff)); + + // Update GPLR + m_GPLR = new_GPLR; + } + break; + + case GPCR: + { + // Determine what pins are configured as output and we want to clear it value now. + uint32_t const temp = (m_GPDR & (value & 0xFFFFFFF)); + + // Update GEDR + m_GEDR |= (m_GFER & (m_GPLR & temp)); + + // Update GPLR + + // :SA-1110 Developer's Manual: p.77: Wei 2004-Jul-1: + // + // To clear an output pin, a one is written to the corresponding bit within the GPCR. + m_GPLR &= ~temp; + } + break; + + case GRER: + m_GRER = (value & 0xFFFFFFF); + break; + + case GFER: + m_GFER = (value & 0xFFFFFFF); + break; + + case GEDR: + // :SA-1110 Developer's Manual: p.79: Wei 2004-Jun-05: + // + // GEDR status bits are cleared by writing a one to them. + // Writing a zero to a GEDR status bit has no effect. + m_GEDR &= ~(value & 0xFFFFFFF); + break; + + case GAFR: + // :SA-1110 Developer's Manual: p.233: Wei 2004-Jun-06: + // + // In active mode, GPIO pins 2..9 are also used. + // Note that the user must configure GPIO pins 2..9 as outputs (for 16-bit/pixel mode) + // by setting the appropriate bits within the GPIO pin direction register (GPDR) + // and GPIO alternate function register (GAFR). + // + // :NOTE: Wei 2004-Jun-06: + // + // ARMware supports only TFT (active) mode now. + + // :SA-1110 Developer's Manual: p.80: Wei 2004-Jun-06: + // + // A bit set in this register indicates that the corresponding GPIO pin + // is to be used for its alternate function. + //assert(LDD_BITS == (value & LDD_BITS)); + + m_GAFR = (value & 0xFFFFFFF); + break; + + default: + break; + } + } + +} // namespace SA1100 diff --git a/WindCore/sa1100/gpio_controller.h b/WindCore/sa1100/gpio_controller.h new file mode 100644 index 0000000..b2af1d1 --- /dev/null +++ b/WindCore/sa1100/gpio_controller.h @@ -0,0 +1,149 @@ +// 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 GPIO_CONTROLLER_H +#define GPIO_CONTROLLER_H +#include +#include + +namespace SA1100 +{ + +class GPIOController { + private: + + // Register location + + enum { + GPLR = 0x90040000, + GPDR = 0x90040004, + GPSR = 0x90040008, + GPCR = 0x9004000C, + GRER = 0x90040010, + GFER = 0x90040014, + GEDR = 0x90040018, + GAFR = 0x9004001C + }; + + // Normal function + + enum { + GAFR_ACTION_BUTTON = (1 << 18) + }; + + // Alternative function + + enum { + LDD_8 = (1 << 2), + LDD_9 = (1 << 3), + LDD_10 = (1 << 4), + LDD_11 = (1 << 5), + LDD_12 = (1 << 6), + LDD_13 = (1 << 7), + LDD_14 = (1 << 8), + LDD_15 = (1 << 9), + + LDD_BITS = (LDD_8 | LDD_9 | LDD_10 | LDD_11 | LDD_12 | LDD_13 | LDD_14 | LDD_15) + }; + + // Attribute + + uint32_t m_GPLR; // GPIO pin-level register + uint32_t m_GPDR; // GPIO pin direction register + uint32_t m_GPSR; // GPIO pin output set register + uint32_t m_GPCR; // GPIO pin output clear register + uint32_t m_GRER; // GPIO rising-edge detect register + uint32_t m_GFER; // GPIO falling-edge detect register + uint32_t m_GEDR; // GPIO edge detect status register + uint32_t m_GAFR; // GPIO alternate function register + + uint32_t m_GPLR_backup; + + // Operation + + void init_register() { + m_GPLR = 0; + m_GPDR = 0; + m_GPSR = 0; + m_GPCR = 0; + m_GRER = 0; + m_GFER = 0; + m_GEDR = 0; + m_GAFR = 0; + } + + public: + + // Life cycle + + GPIOController() { + init_register(); + } + + // Operation + + inline void run() { + uint32_t const temp = m_GPLR ^ m_GPLR_backup; + + if (temp != 0) { + m_GEDR |= (m_GRER & (m_GPLR & temp)); + m_GEDR |= (m_GFER & (m_GPLR_backup & temp)); + + m_GPLR_backup = m_GPLR; + } + } + + void reset(); + + inline uint32_t get_interrupt_status() const { return m_GEDR; } + + // :NOTE: Wei 2004-Jan-11: + // + // if bit 18 of GPLR == 0: action button is pressed. + // if bit 18 of GPLR == 1: action button is released. + inline void press_action_button() { + // :SA-1110 Developer's Manual: p.80: Wei 2004-Jul-1: + // + // A zero in GAFR indicates that the corresponding GPIO pin is to be used for its normal GPIO function. + if (0 == (m_GAFR & GAFR_ACTION_BUTTON)) + { + m_GPLR &= ~GAFR_ACTION_BUTTON; + } + } + inline void flip_gpio() { + if (m_GPLR & 0x400) { + m_GPLR &= ~0x400; + } else { + m_GPLR |= 0x400; + } + } + + inline void release_action_button() { + if (0 == (m_GAFR & GAFR_ACTION_BUTTON)) + { + m_GPLR |= GAFR_ACTION_BUTTON; + } + } + + uint32_t get_data(uint32_t const address) const; + + void put_data(uint32_t const address, uint32_t const value); +}; + +} // namespace SA1100 + +#endif // GPIO_CONTROLLER_H diff --git a/WindCore/sa1100/interrupt_controller.cpp b/WindCore/sa1100/interrupt_controller.cpp new file mode 100644 index 0000000..3f6e433 --- /dev/null +++ b/WindCore/sa1100/interrupt_controller.cpp @@ -0,0 +1,547 @@ +// 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 "interrupt_controller.h" + +namespace SA1100 { + + IntController::IntController(LCDController* const lcdController, + OsTimer* const osTimer, + GPIOController* const gpioController, + Serial3* const serial3) + : mOsTimer(osTimer), + mLCDController(lcdController), + mGPIOController(gpioController), + mSerial3(serial3) { + init_register(); + } + + //============================== Operation ================================== + + void IntController::reset() { + m_ICIP = 0; + + // :NOTE: Wei 2004-Apr-25: + // + // Although the SA-1110 Developer's Manual doesn't specify whether ICFP will be + // initialized to 0 when reset. However, SA-1110 Developer's Manual does specify + // ICIP will be initialized to 0 when reset. + // Thus I _assume_ this is a bug in the SA-1110 Developer's Manual, and will do + // the initialization by my self. + + // :NOTE: Wei 2004-Jul-20; + // + // It is really a bug. + // According to the Intel-StrongARM SA-1110 Microprocessor Specification Update + // (December 2001), p.37: + // + // Changed the reset values for the Interrupt Controller IRQ and FIQ Pending + // Registers (ICIP and ICFP) from undefined to 0. + m_ICFP = 0; + + m_ICCR = 0; + //m_ICMR = ~0; + + // :NOTE: Wei 2004-Jun-06: + // + // Althought there is no words in SA-1110 Developer's manual says that + // ICPR will be set to 0 when reset. + // However, I think it should set to 0 when reset to indicate that no + // pending interrupt exist. + m_ICPR = 0; + } + + void IntController::run() { + // check GPIO interrupt + + { + uint32_t const gpio_status = mGPIOController->get_interrupt_status(); + + if (gpio_status != 0) + { + m_ICPR &= ~GPIO_0_10_EDGE_BIT_MASK; + m_ICPR |= (gpio_status & GPIO_0_10_EDGE_BIT_MASK); + + if ((gpio_status & ~GPIO_0_10_EDGE_BIT_MASK) != 0) + { + m_ICPR |= GPIO_11_27_EDGE_BIT_MASK; + } + else + { + m_ICPR &= ~GPIO_11_27_EDGE_BIT_MASK; + } + } + } + + // check OS timer + + { + uint32_t const os_timer_status = mOsTimer->get_interrupt_status(); + + switch (os_timer_status) { + case 0: // 0000 + { + m_ICPR &= ~(OS_TIMER_0_BIT_MASK | + OS_TIMER_1_BIT_MASK | + OS_TIMER_2_BIT_MASK | + OS_TIMER_3_BIT_MASK); + } + break; + + case OsTimer::OSMR0_MASK: // 0001 + { + m_ICPR |= OS_TIMER_0_BIT_MASK; + + m_ICPR &= ~(OS_TIMER_1_BIT_MASK | + OS_TIMER_2_BIT_MASK | + OS_TIMER_3_BIT_MASK); + } + break; + + case OsTimer::OSMR1_MASK: // 0010 + { + m_ICPR |= OS_TIMER_1_BIT_MASK; + + m_ICPR &= ~(OS_TIMER_0_BIT_MASK | + OS_TIMER_2_BIT_MASK | + OS_TIMER_3_BIT_MASK); + } + break; + + case (OsTimer::OSMR0_MASK | + OsTimer::OSMR1_MASK): // 0011 + { + m_ICPR |= OS_TIMER_0_BIT_MASK; + m_ICPR |= OS_TIMER_1_BIT_MASK; + + m_ICPR &= ~(OS_TIMER_2_BIT_MASK | + OS_TIMER_3_BIT_MASK); + } + break; + + case OsTimer::OSMR2_MASK: // 0100 + { + m_ICPR |= OS_TIMER_2_BIT_MASK; + + m_ICPR &= ~(OS_TIMER_0_BIT_MASK | + OS_TIMER_1_BIT_MASK | + OS_TIMER_3_BIT_MASK); + } + break; + + case (OsTimer::OSMR0_MASK | + OsTimer::OSMR2_MASK): // 0101 + { + m_ICPR |= OS_TIMER_0_BIT_MASK; + m_ICPR |= OS_TIMER_2_BIT_MASK; + + m_ICPR &= ~(OS_TIMER_1_BIT_MASK | + OS_TIMER_3_BIT_MASK); + } + break; + + case (OsTimer::OSMR1_MASK | + OsTimer::OSMR2_MASK): // 0110 + { + m_ICPR |= OS_TIMER_1_BIT_MASK; + m_ICPR |= OS_TIMER_2_BIT_MASK; + + m_ICPR &= ~(OS_TIMER_0_BIT_MASK | + OS_TIMER_3_BIT_MASK); + } + break; + + case (OsTimer::OSMR0_MASK | + OsTimer::OSMR1_MASK | + OsTimer::OSMR2_MASK): // 0111 + { + m_ICPR |= OS_TIMER_0_BIT_MASK; + m_ICPR |= OS_TIMER_1_BIT_MASK; + m_ICPR |= OS_TIMER_2_BIT_MASK; + + m_ICPR &= ~OS_TIMER_3_BIT_MASK; + } + break; + + case OsTimer::OSMR3_MASK: // 1000 + { + m_ICPR |= OS_TIMER_3_BIT_MASK; + + m_ICPR &= ~(OS_TIMER_0_BIT_MASK | + OS_TIMER_1_BIT_MASK | + OS_TIMER_2_BIT_MASK); + } + break; + + case (OsTimer::OSMR0_MASK | + OsTimer::OSMR3_MASK): // 1001 + { + m_ICPR |= OS_TIMER_0_BIT_MASK; + m_ICPR |= OS_TIMER_3_BIT_MASK; + + m_ICPR &= ~(OS_TIMER_1_BIT_MASK | + OS_TIMER_2_BIT_MASK); + } + break; + + case (OsTimer::OSMR1_MASK | + OsTimer::OSMR3_MASK): // 1010 + { + m_ICPR |= OS_TIMER_1_BIT_MASK; + m_ICPR |= OS_TIMER_3_BIT_MASK; + + m_ICPR &= ~(OS_TIMER_0_BIT_MASK | + OS_TIMER_2_BIT_MASK); + } + break; + + case (OsTimer::OSMR0_MASK | + OsTimer::OSMR1_MASK | + OsTimer::OSMR3_MASK): // 1011 + { + m_ICPR |= OS_TIMER_0_BIT_MASK; + m_ICPR |= OS_TIMER_1_BIT_MASK; + m_ICPR |= OS_TIMER_3_BIT_MASK; + + m_ICPR &= ~OS_TIMER_2_BIT_MASK; + } + break; + + case (OsTimer::OSMR2_MASK | + OsTimer::OSMR3_MASK): // 1100 + { + m_ICPR |= OS_TIMER_2_BIT_MASK; + m_ICPR |= OS_TIMER_3_BIT_MASK; + + m_ICPR &= ~(OS_TIMER_0_BIT_MASK | + OS_TIMER_1_BIT_MASK); + } + break; + + case (OsTimer::OSMR0_MASK | + OsTimer::OSMR2_MASK | + OsTimer::OSMR3_MASK): // 1101 + { + m_ICPR |= OS_TIMER_0_BIT_MASK; + m_ICPR |= OS_TIMER_2_BIT_MASK; + m_ICPR |= OS_TIMER_3_BIT_MASK; + + m_ICPR &= ~OS_TIMER_1_BIT_MASK; + } + break; + + case (OsTimer::OSMR1_MASK | + OsTimer::OSMR2_MASK | + OsTimer::OSMR3_MASK): // 1110 + { + m_ICPR |= OS_TIMER_1_BIT_MASK; + m_ICPR |= OS_TIMER_2_BIT_MASK; + m_ICPR |= OS_TIMER_3_BIT_MASK; + + m_ICPR &= ~OS_TIMER_0_BIT_MASK; + } + break; + + case (OsTimer::OSMR0_MASK | + OsTimer::OSMR1_MASK | + OsTimer::OSMR2_MASK | + OsTimer::OSMR3_MASK): // 1111 + { + m_ICPR |= (OS_TIMER_0_BIT_MASK | + OS_TIMER_1_BIT_MASK | + OS_TIMER_2_BIT_MASK | + OS_TIMER_3_BIT_MASK); + } + break; + + default: + break; + } + } + + // check RTC + + // { + // uint32_t const rtc_status = mp_rtc->get_interrupt_status(); + + // switch (rtc_status) + // { + // case 0: + // m_ICPR &= ~(RTC_ALARM_BIT_MASK | RTC_HZ_BIT_MASK); + // break; + + // case RTC::STATUS_AL_BIT: + // m_ICPR |= RTC_ALARM_BIT_MASK; + // m_ICPR &= ~RTC_HZ_BIT_MASK; + // break; + + // case RTC::STATUS_HZ_BIT: + // m_ICPR |= RTC_HZ_BIT_MASK; + // m_ICPR &= ~RTC_ALARM_BIT_MASK; + // break; + + // case (RTC::STATUS_AL_BIT | RTC::STATUS_HZ_BIT): + // m_ICPR |= (RTC_ALARM_BIT_MASK | RTC_HZ_BIT_MASK); + // break; + + // default: + // assert(!"Should not reach here."); + // break; + // } + // } + + // check LCD controller + + { + uint32_t const lcd_status = mLCDController->get_interrupt_status(); + + if (0 == lcd_status) + { + m_ICPR &= ~LCD_SERVICE_BIT_MASK; + } + else + { + if ((lcd_status & ~(LCDController::LCSR_BAU | LCDController::LCSR_LDD)) != 0) + { + m_ICPR |= LCD_SERVICE_BIT_MASK; + } + else + { + switch (lcd_status & (LCDController::LCSR_BAU | LCDController::LCSR_LDD)) + { + case 0: + // If we go here, means (lcd_status == 0), however, (lcd_status == 0) should + // go to the upper 'if' block, thus if we go here, it is a bug. + break; + + case LCDController::LCSR_BAU: + { + uint32_t const ctrl_0 = mLCDController->get_ctrl_reg_0(); + + if (0 == (ctrl_0 & LCDController::LCCR0_BAM)) + { + // Enable BAU + m_ICPR |= LCD_SERVICE_BIT_MASK; + } + else + { + m_ICPR &= ~LCD_SERVICE_BIT_MASK; + } + } + break; + + case LCDController::LCSR_LDD: + { + uint32_t const ctrl_0 = mLCDController->get_ctrl_reg_0(); + + if (0 == (ctrl_0 & LCDController::LCCR0_LDM)) + { + // Enable LDD + m_ICPR |= LCD_SERVICE_BIT_MASK; + } + else + { + m_ICPR &= ~LCD_SERVICE_BIT_MASK; + } + } + break; + + case (LCDController::LCSR_BAU | LCDController::LCSR_LDD): + { + uint32_t const ctrl_0 = mLCDController->get_ctrl_reg_0(); + + switch (ctrl_0 & (LCDController::LCCR0_BAM | LCDController::LCCR0_LDM)) + { + case 0: + case LCDController::LCCR0_BAM: + case LCDController::LCCR0_LDM: + // Enable BAU + m_ICPR |= LCD_SERVICE_BIT_MASK; + break; + + case (LCDController::LCCR0_BAM | LCDController::LCCR0_LDM): + m_ICPR &= ~LCD_SERVICE_BIT_MASK; + break; + + default: + break; + } + } + break; + } + } + } + } + + // Check UART in the serial port 1, 2 & 3 + // check_UART_interrupt(mp_serial_1->get_UART()); + // check_UART_interrupt(mp_serial_2->get_UART()); + check_UART_interrupt(mSerial3->get_UART()); + + // Determine IRQ & FIQ + + if (m_ICPR != 0) + { + uint32_t const temp = m_ICPR & m_ICMR; + + m_ICIP = temp & (~m_ICLR); + m_ICFP = temp & m_ICLR; + + // if (true == mp_core->is_in_idle_mode()) + // { + // // We have at least one interrupt. + // if (0 == (m_ICCR & ICCR_DIM_BITMASK)) + // { + // // :SA-1110 Developer's Manual: p.89: Wei 2004-May-09: + // // + // // All enabled interrupts will bring the SA-1110 out of idle mode. + // mp_core->exit_idle_mode(); + // } + // else + // { + // if (temp != 0) + // { + // // :SA-1110 Developer's Manual: p.89: Wei 2004-May-09: + // // + // // Only enabled and unmasked (as defined in the ICMR) will bring + // // the SA-1110 out of idle mode. + // mp_core->exit_idle_mode(); + // } + // } + // } + } else { + // Optimize the condition of no pending interrupt. + + // m_ICPR == 0: We have no pending interrupt. + m_ICIP = 0; + m_ICFP = 0; + } + } + + uint32_t IntController::get_data(uint32_t const address) const { + switch (address) { + case ICIP: return m_ICIP; + case ICMR: return m_ICMR; + case ICLR: return m_ICLR; + case ICCR: return m_ICCR; + case ICFP: return m_ICFP; + case ICPR: return m_ICPR; + + default: + return 0; + } + } + + void IntController::put_data(uint32_t const address, uint32_t const value) { + switch (address) + { + case ICIP: + // :SA-1110 Developer's Manual: Wei 2003-Dec-09: + // + // ICIP is a read-only register + break; + + case ICMR: + m_ICMR = value; + break; + + case ICLR: + m_ICLR = value; + break; + + case ICCR: + // :SA-1110 Developer's Manual: p.89: Wei 2004-May-09: + // + // bits[31:1] are reserved. + m_ICCR = (value & 0x1); + break; + + case ICFP: + // :SA-1110 Developer's Manual: Wei 2003-Dec-09: + // + // ICFP is a read-only register + break; + + case ICPR: + // :SA-1110 Developer's Manual: Wei 2003-Dec-09: + // + // ICPR is a read-only register + break; + + default: + break; + } + } + + + void IntController::check_UART_interrupt(Uart const &uart) { + uint32_t const UART_status = uart.get_interrupt_status(); + + if (0 == UART_status) + { + m_ICPR &= ~SERIAL_3_BIT_MASK; + } + else + { + uint32_t const ctrl_3 = uart.get_ctrl_reg_3(); + + switch (ctrl_3 & (Uart::UTCR3_TIE | + Uart::UTCR3_RIE)) + { + case 0: + break; + + case Uart::UTCR3_TIE: + if ((UART_status & Uart::UTSR0_TFS) != 0) + { + m_ICPR |= SERIAL_3_BIT_MASK; + } + else + { + m_ICPR &= ~SERIAL_3_BIT_MASK; + } + break; + + case Uart::UTCR3_RIE: + if ((UART_status & (Uart::UTSR0_RFS | + Uart::UTSR0_RID)) != 0) + { + m_ICPR |= SERIAL_3_BIT_MASK; + } + else + { + m_ICPR &= ~SERIAL_3_BIT_MASK; + } + break; + + case (Uart::UTCR3_TIE | Uart::UTCR3_RIE): + if ((UART_status & (Uart::UTSR0_TFS | + Uart::UTSR0_RFS | + Uart::UTSR0_RID)) != 0) + { + m_ICPR |= SERIAL_3_BIT_MASK; + } + else + { + m_ICPR &= ~SERIAL_3_BIT_MASK; + } + break; + } + } + } + +} // namespace SA1100 diff --git a/WindCore/sa1100/interrupt_controller.h b/WindCore/sa1100/interrupt_controller.h new file mode 100644 index 0000000..ac4fe50 --- /dev/null +++ b/WindCore/sa1100/interrupt_controller.h @@ -0,0 +1,158 @@ +// 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 INTERRUPT_CONTROLLER_H +#define INTERRUPT_CONTROLLER_H + +#include + +#include "gpio_controller.h" +#include "lcd_controller.h" +#include "os_timer.h" +#include "serial_3.h" +#include "uart.h" + +namespace SA1100 { + +class IntController { + public: + + // Interrupt Bit Mask + + enum IntCtrlBitMaskEnum + { + GPIO_0_EDGE_BIT_MASK = (1 << 0), + GPIO_1_EDGE_BIT_MASK = (1 << 1), + GPIO_2_EDGE_BIT_MASK = (1 << 2), + GPIO_3_EDGE_BIT_MASK = (1 << 3), + GPIO_4_EDGE_BIT_MASK = (1 << 4), + GPIO_5_EDGE_BIT_MASK = (1 << 5), + GPIO_6_EDGE_BIT_MASK = (1 << 6), + GPIO_7_EDGE_BIT_MASK = (1 << 7), + GPIO_8_EDGE_BIT_MASK = (1 << 8), + GPIO_9_EDGE_BIT_MASK = (1 << 9), + GPIO_10_EDGE_BIT_MASK = (1 << 10), + + GPIO_0_10_EDGE_BIT_MASK = (GPIO_0_EDGE_BIT_MASK | + GPIO_1_EDGE_BIT_MASK | + GPIO_2_EDGE_BIT_MASK | + GPIO_3_EDGE_BIT_MASK | + GPIO_4_EDGE_BIT_MASK | + GPIO_5_EDGE_BIT_MASK | + GPIO_6_EDGE_BIT_MASK | + GPIO_7_EDGE_BIT_MASK | + GPIO_8_EDGE_BIT_MASK | + GPIO_9_EDGE_BIT_MASK | + GPIO_10_EDGE_BIT_MASK), + + GPIO_11_27_EDGE_BIT_MASK = (1 << 11), + + LCD_SERVICE_BIT_MASK = (1 << 12), + + SERIAL_1_BIT_MASK = (1 << 15), + SERIAL_2_BIT_MASK = (1 << 16), + SERIAL_3_BIT_MASK = (1 << 17), + + OS_TIMER_0_BIT_MASK = (1 << 26), + OS_TIMER_1_BIT_MASK = (1 << 27), + OS_TIMER_2_BIT_MASK = (1 << 28), + OS_TIMER_3_BIT_MASK = (1 << 29), + + RTC_HZ_BIT_MASK = (1 << 30), + RTC_ALARM_BIT_MASK = (1 << 31) + }; + typedef enum IntCtrlBitMaskEnum IntCtrlBitMaskEnum; + + private: + + // Attribute + OsTimer* const mOsTimer; + LCDController* const mLCDController; + GPIOController* const mGPIOController; + Serial3* const mSerial3; + + // Register location + + enum + { + ICIP = 0x90050000, + ICMR = 0x90050004, + ICLR = 0x90050008, + ICCR = 0x9005000C, + ICFP = 0x90050010, + ICPR = 0x90050020 + }; + + // Register + + uint32_t m_ICIP; + uint32_t m_ICMR; + uint32_t m_ICLR; + uint32_t m_ICCR; + uint32_t m_ICFP; + uint32_t m_ICPR; + + enum + { + ICCR_DIM_BITMASK = 0x1 + }; + + // Operation + + void + init_register() + { + m_ICIP = 0; + m_ICMR = 0; + m_ICLR = 0; + m_ICCR = 0; + m_ICFP = 0; + m_ICPR = 0; + } + + void check_UART_interrupt(Uart const &uart); + + public: + + // Life cycle + + IntController(LCDController* const lcdController, + OsTimer* const osTimer, + GPIOController* const gpioController, + Serial3* const serial3); + + // Operation + + void run(); + void reset(); + + uint32_t get_data(uint32_t const address) const; + + void put_data(uint32_t const address, uint32_t const value); + + inline bool have_pending_irq() const { + return (0 == m_ICIP) ? false : true; + } + + inline bool have_pending_fiq() const { + return (0 == m_ICFP) ? false : true; + } +}; + +} // namespace SA1100 + +#endif // INTERRUPT_CONTROLLER_H diff --git a/WindCore/sa1100/lcd_controller.cpp b/WindCore/sa1100/lcd_controller.cpp new file mode 100644 index 0000000..3981889 --- /dev/null +++ b/WindCore/sa1100/lcd_controller.cpp @@ -0,0 +1,207 @@ +// 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 + +#include "lcd_controller.h" + +namespace SA1100 { + +LCDController::LCDController() { + init_register(); +} + +void LCDController::reset() { + m_LCCR0 = 0; + m_LCCR1 = 0; + m_LCCR2 = 0; + m_LCCR3 = 0; + m_LCSR = 0; + mEnabled = false; +} + +uint32_t LCDController::get_data32(uint32_t const address) const { + std::cout << "LCD: get value at " << std::hex << address << std::endl; + + switch (address) { + case LCCR0: return m_LCCR0; + case LCCR1: return m_LCCR1; + case LCCR2: return m_LCCR2; + case LCCR3: return m_LCCR3; + case DBAR1: return m_DBAR1; + case DCAR1: return m_DCAR1; + case DBAR2: return m_DBAR2; + case DCAR2: return m_DCAR2; + case LCSR: return m_LCSR; + + default: + return 0; + } +} + +void LCDController::put_data32(uint32_t const address, uint32_t const value) + { + switch (address) { + case LCCR0: + { + std::cout << "LCD: LCCR0: " << std::hex << value << std::endl; + + uint32_t const diff = (m_LCCR0 ^ value); + + if ((diff & LCCR0_LEN) != 0) + { + if (0 == (value & LCCR0_LEN)) + { + std::cout << "LCD: disable lcd" << std::endl; + mEnabled = false; + m_LCSR |= LCSR_LDD; + + // mp_machine_screen->disable_drawing(); + // mp_memory->disable_drawing(); + } + else + { + std::cout << "LCD: enable lcd" << std::endl; + mEnabled = true; + + // :NOTE: Wei 2004-Jun-06: + // + // ARMware supports only TFT (active) mode now. + // assert(LCCR0_PAS == (value & LCCR0_PAS)); + + // :SA-1110 Developer's Manual: Wei 2003-Dec-08: + // + // Value in DBAR1(or 2) is transferred to DCAR1(or 2) when LCD is first enabled (LEN = 0->1). + m_DCAR1 = m_DBAR1; + m_DCAR2 = m_DBAR2; + + // :SA-1110 Developer's Manual: p.249: Wei 2004-Jan-15: + // + // The base address update status (BAU) is a read/write status bit + // that is set after the contents of the DMA base address register 1 + // are transferred to the DMA current Address register 1. + m_LCSR |= LCSR_BAU; + + // mp_machine_screen->enable_drawing(); + // mp_memory->enable_drawing(); + } + } + + if ((diff & LCCR0_SDS) != 0) { + // No support for dual panels, ignore + } + + m_LCCR0 = (value & (0xFFFFF ^ (1 << 6))); + } + break; + + case LCCR1: + std::cout << "LCD: LCCR1: " << std::hex << value << std::endl; + + // :SA-1110 Developer's Manual: Wei 2004-Jan-13: + // + // Note that the bottom four bits of PPL are not implemented and + // therefore are not writable. Reads of these bits return zeros + // because the LCD controller only supports displays that are a + // multiple of 16 pixels wide. + m_LCCR1 = (value & 0xFFFFFFF0); + break; + + case LCCR2: + std::cout << "LCD: LCCR2: " << std::hex << value << std::endl; + + m_LCCR2 = value; + break; + + case LCCR3: + std::cout << "LCD: LCCR3: " << std::hex << value << std::endl; + + m_LCCR3 = (value & 0xFFFFFF); + break; + + case DBAR1: + std::cout << "LCD: DBAR1: " << std::hex << value << std::endl; + + // :SA-1110 Developer's Manual: p.245: Wei 2003-Dec-08: + // + // Addresses programmed in the base address register must be aligned + // on quadword boundaries; + // the least significant four bits (DBAR1[3:0]) must always be written with zeros. + // assert(0 == (value & 0xF)); + + // :NOTE: Wei 2004-Jan-15: + // + // If we want to change the frame buffer address, + // we are likely need to update the whole screen. + if (m_DBAR1 != value) + { + m_DBAR1 = value; + // mp_machine_screen->set_frame_buffer_addr_1(m_DBAR1); + // mp_machine_screen->template update_panel(); + // mp_memory->set_frame_buffer_addr_1(m_DBAR1); + } + break; + + case DBAR2: + std::cout << "LCD: DBAR2: " << std::hex << value << std::endl; + + // :SA-1110 Developer's Manual: p.245: Wei 2003-Dec-08: + // + // Addresses programmed in the base address register must be aligned + // on quadword boundaries; + // the least significant four bits (DBAR1[3:0]) must always be written with zeros. + // assert(0 == (value & 0xF)); + + if (m_DBAR2 != value) + { + m_DBAR2 = value; + + // mp_machine_screen->set_frame_buffer_addr_2(m_DBAR2); + // mp_machine_screen->template update_panel(); + + // mp_memory->set_frame_buffer_addr_2(m_DBAR2); + } + break; + + case DCAR1: + case DCAR2: + // :SA-1110 Developer's Manual: p.247: Wei 2004-Jun-06: + // :SA-1110 Developer's Manual: p.248: Wei 2004-Jun-06: + // + // These are read-only registers. + // assert(!"Should not reach here."); + break; + + case LCSR: + std::cout << "LCD: LCSR: " << std::hex << value << std::endl; + + // :SA-1110 Developer's Manual: p.248: Wei 2004-Jun-06: + // + // Status bits are referred to as 'sticky' (once set by hardware, + // they must be cleared by software). Writing a 1 to a sticky status bit clears it; + // writing a zero has no effect. + // Read-only flags are set and cleared by hardware; writes have no effect. + + m_LCSR &= ~(m_LCSR & (value & LCSR_READ_WRITE_BITMASK)); + break; + + default: + // assert(!"Should not reach here."); + break; + } + } +} // namespace SA1100 \ No newline at end of file diff --git a/WindCore/sa1100/lcd_controller.h b/WindCore/sa1100/lcd_controller.h new file mode 100644 index 0000000..e8f0c0f --- /dev/null +++ b/WindCore/sa1100/lcd_controller.h @@ -0,0 +1,192 @@ +// 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 LCD_CONTROLLER_H +#define LCD_CONTROLLER_H + +#include + +namespace SA1100 { + +class LCDController { +public: + enum { + LCCR0_LEN = (1 << 0), // LCD controller enable + LCCR0_CMS = (1 << 1), // Color/monochrome select. + LCCR0_SDS = (1 << 2), // Single-/dual-panel display select. + LCCR0_LDM = (1 << 3), // LCD disable done mask + LCCR0_BAM = (1 << 4), // Base address update interrupt mask + LCCR0_ERM = (1 << 5), // Error mask. + LCCR0_PAS = (1 << 7), // Passive/active display select + LCCR0_BLE = (1 << 8), // Big/little endian select. + LCCR0_DPD = (1 << 9), // Double-pixel data pin mode. + LCCR0_VCS = ((1 << 10) | (1 << 11)), // Vertical slant line correction. + LCCR0_PDD = ((1 << 12) | (1 << 13) | + (1 << 14) | (1 << 15) | + (1 << 16) | (1 << 17) | + (1 << 18) | (1 << 19)) // Palette DMA request delay. + }; + + enum { + LCSR_LDD = (1 << 0), // LCD disable done status. + LCSR_BAU = (1 << 1), // Base address update flag (read-only, maskable interrupt) + LCSR_BER = (1 << 2), // Bus error status. + LCSR_ABC = (1 << 3), // AC bias count status. + + // :NOTE: Wei 2004-Jun-06: + // + // I think in ARMware, we shouldn't raise the following bits. + // As a result, no interrupts should be raised because of the following bits. + + LCSR_IOL = (1 << 4), // Input FIFO overrun lower panel status. + LCSR_IUL = (1 << 5), // Input FIFO underrun lower panel status. + LCSR_IOU = (1 << 6), // Input FIFO overrun upper panel status. + LCSR_IUU = (1 << 7), // Input FIFO underrun upper panel status. + LCSR_OOL = (1 << 8), // Output FIFO overrun lower panel status. + LCSR_OUL = (1 << 9), // Output FIFO underrun lower panel status. + LCSR_OOU = (1 << 10), // Output FIFO overrun upper panel status. + LCSR_OUU = (1 << 11), // Output FIFO underrun upper panel status. + + // :NOTE: Wei 2004-Jun-30: + // + // Althought page 249 in the Intel SA-1110 Developer's Manual says that BAU flag is read-only, + // however, in the same page (p.249), it says that BAU flag can be cleared when it is written + // to a 1. + // After searching google, I find a post related this: + // + // �H��̡Gbratfi_t@my-deja.com (bratfi_t@my-deja.com) + // �D���GRe: BAU flag on SA1100 + // ���W�׾¡Gcomp.sys.arm + // ����G1999/08/31 + // + // ... + // According to the manual, the BAU flag is read-only, but it can be cleared like a status bit! + // ... + // + // Thus I include BAU in the following constant. + LCSR_READ_WRITE_BITMASK = (LCSR_LDD | + LCSR_BAU | + LCSR_BER | + LCSR_ABC | + LCSR_IOL | + LCSR_IUL | + LCSR_IOU | + LCSR_IUU | + LCSR_OOL | + // LCSR_OUL | + LCSR_OOU | + LCSR_OUU) + }; + + enum + { + LCCR0 = 0xB0100000, + LCSR = 0xB0100004, + DBAR1 = 0xB0100010, + DCAR1 = 0xB0100014, + DBAR2 = 0xB0100018, + DCAR2 = 0xB010001C, + LCCR1 = 0xB0100020, + LCCR2 = 0xB0100024, + LCCR3 = 0xB0100028 + }; + +private: + // Operation + + uint32_t CalculateFrameBufferEndAddress(); + + bool mEnabled; + + // Register location + + + // Register + + uint32_t m_LCCR0; // LCD controller control register 0 + uint32_t m_LCSR; // LCD controller status register 1 + uint32_t m_DBAR1; // DMA channel 1 base address register + uint32_t m_DCAR1; // DMA channel 1 current address register + uint32_t m_DBAR2; // DMA channel 2 base address register + uint32_t m_DCAR2; // DMA channel 2 current address register + uint32_t m_LCCR1; // LCD controller control register 1 + uint32_t m_LCCR2; // LCD controller control register 2 + uint32_t m_LCCR3; // LCD controller control register 3 + + // Operation + + void init_register() { + m_LCCR0 = 0; + m_LCSR = 0; + m_DBAR1 = 0; + m_DCAR1 = 0; + m_DBAR2 = 0; + m_DCAR2 = 0; + m_LCCR1 = 0; + m_LCCR2 = 0; + m_LCCR3 = 0; + } + +public: + + LCDController(); + void reset(); + + bool isLCDEnabled() { return mEnabled; } + + unsigned int getWidth() { return (m_LCCR1 & 0x3FF) + 16; } + unsigned int getHeight() { return (m_LCCR2 & 0x3FF) + 1; } + + inline void finish_one_frame() { + // assert(LCCR0_LEN == (m_LCCR0 & LCCR0_LEN)); + m_DCAR1 = m_DBAR1; + m_DCAR2 = m_DBAR2; + + // :SA-1110 Developer's Manual: Wei 2003-Dec-08: + // + // Value in DBAR1(or 2) is transferred to DCAR1(or 2) when LCD is first enabled (LEN = 0->1) + // and when the current address pointer value equals the end-of-frame buffer. + + // :NOTE: Wei 2004-Jan-15: + // + // Althought I don't update the m_DCAR1(or 2) when (m_DBAR1 == m_DCAR1) here, + // however, they are actually transfered in the real SA-1110, + // thus the following LCSR_BAU setting operation are always performed. + + // :SA-1110 Developer's Manual: p.249: Wei 2004-Jan-15: + // + // The base address update status (BAU) is a read/write status bit + // that is set after the contents of the DMA base address register 1 + // are transferred to the DMA current Address register 1 + m_LCSR |= LCSR_BAU; + } + + inline uint32_t get_interrupt_status() const { + return m_LCSR; + } + + inline uint32_t get_ctrl_reg_0() const { + return m_LCCR0; + } + + uint32_t get_data32(uint32_t const address) const; + void put_data32(uint32_t const address, uint32_t const value); +}; + +} // namespace SA1100 + +#endif // LCD_CONTROLLER_H diff --git a/WindCore/sa1100/os_timer.cpp b/WindCore/sa1100/os_timer.cpp new file mode 100644 index 0000000..10bc5e5 --- /dev/null +++ b/WindCore/sa1100/os_timer.cpp @@ -0,0 +1,181 @@ +// 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" + +namespace SA1100 { + + OsTimer::OsTimer() + { + init_register(); + } + + //============================== Operation ================================== + + 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. + m_OWER = 0; + m_has_enabled_watchdog_timer = false; + + // :SA-1110 Developer's Manual: p.97: Wei 2004-Apr-24: + // + // the value of bits 0 ~ 3 of OSSR register are unknown at reset. + m_OSSR &= 0xF; + + m_OIER = 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 (m_OIER & 0xF) + { + case 0: + break; + + default: + compare_and_set_status(m_OIER & 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. + ++m_OSCR; + } + + uint32_t OsTimer::get_data(uint32_t const address) const { + switch (address) + { + case OSMR0: + return m_OSMR[0]; + + case OSMR1: + return m_OSMR[1]; + + case OSMR2: + return m_OSMR[2]; + + case OSMR3: + return m_OSMR[3]; + + case OSCR: + return m_OSCR; + + case OSSR: + return m_OSSR; + + case OWER: + return m_OWER; + + case OIER: + return m_OIER; + + default: + return 0; + } + } + + void OsTimer::put_data(uint32_t const address, uint32_t const value) { + switch (address) + { + case OSMR0: + m_OSMR[0] = value; + break; + + case OSMR1: + m_OSMR[1] = value; + break; + + case OSMR2: + m_OSMR[2] = value; + break; + + case OSMR3: + m_OSMR[3] = value; + break; + + case OSCR: + m_OSCR = value; + break; + + case OSSR: + // :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: + m_OSSR &= ~(value & 0xF); + break; + } + break; + + case OWER: + // :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. + if (false == m_has_enabled_watchdog_timer) + { + if (WME_BITMASK == (value & WME_BITMASK)) + { + // :SA-1110 Developer's Manual: p.98: Wei 2004-Apr-25: + // + // The user must clear OSSR:M3 before setting up a watchdog reset. + //assert(0 == (m_OSSR & OSMRTraits::BITMASK)); + + m_OWER |= WME_BITMASK; + m_has_enabled_watchdog_timer = true; + } + } + break; + + case OIER: + // :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. + m_OIER = (value & 0xF); + break; + + default: + break; + } + } + +} // namespace SA1100 diff --git a/WindCore/sa1100/os_timer.h b/WindCore/sa1100/os_timer.h new file mode 100644 index 0000000..3e10a6b --- /dev/null +++ b/WindCore/sa1100/os_timer.h @@ -0,0 +1,113 @@ +// 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 +#include + +namespace SA1100 { + +class OsTimer { + private: + + static uint32_t const WME_BITMASK = 0x1; // Watchdog Match Enable bit mask + + bool m_has_enabled_watchdog_timer; + + uint32_t m_OSMR[4]; // OS timer match register 0 ~ 3 + uint32_t m_OSCR; // OS timer counter register + uint32_t m_OSSR; // OS timer status register + uint32_t m_OWER; // OS timer watchdog enable register + uint32_t m_OIER; // OS timer interrupt enable register + + void init_register() { + for (uint32_t i = 0; i < 4; ++i) + { + m_OSMR[i] = 0; + } + + m_OSCR = 0; + m_OSSR = 0; + m_OWER = 0; + m_OIER = 0; + } + + inline void compare_and_set_status(uint32_t osmrs) { + if (osmrs & OSMR0_MASK) { + if (m_OSCR == m_OSMR[0]) { + m_OSSR |= OSMR0_MASK; + } + } + if (osmrs & OSMR1_MASK) { + if (m_OSCR == m_OSMR[1]) { + m_OSSR |= OSMR1_MASK; + } + } + if (osmrs & OSMR2_MASK) { + if (m_OSCR == m_OSMR[2]) { + m_OSSR |= OSMR2_MASK; + } + } + if (osmrs & OSMR3_MASK) { + if (m_has_enabled_watchdog_timer) { + // TODO: implement watchdog + } else { + if (m_OSCR == m_OSMR[3]) { + printf("OSMR3 triggered\n"); + m_OSSR |= OSMR3_MASK; + } + } + } + } + + public: + 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) + }; + + OsTimer(); + + void reset(); + void tick(); + + inline uint32_t get_interrupt_status() const { + return m_OSSR; + } + + uint32_t get_data(uint32_t const address) const; + void put_data(uint32_t const address, uint32_t const value); +}; + +} // namespace SA1100 + +#endif // OS_TIMER_H diff --git a/WindCore/sa1100/power_manager.cpp b/WindCore/sa1100/power_manager.cpp new file mode 100644 index 0000000..33a9e45 --- /dev/null +++ b/WindCore/sa1100/power_manager.cpp @@ -0,0 +1,84 @@ +// 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 "power_manager.h" + +namespace SA1100 { + +void PowerManager::reset() { + mPMCR = 0; + mPCFR = 0; + mPPCR = 0; + mPWER = 3; + mPSSR = 0; + + // :NOTE: Wei 2003-Dec-11: + // + // I assume all of the bits which have unknown values when reset have value 0. + mPGSR = 0; + + // :NOTE: Wei 2004-Jan-16: + // + // Because ARMware is not a real machine, thus I should set OOK bit in the + // POSR register after reset immediately. + mPOSR = POSR_OOK; +} + +uint32_t PowerManager::get_data(uint32_t const address) const { + switch (address) { + case PMCR: return mPMCR; + case PSSR: return mPSSR; + case PSPR: return mPSPR; + case PWER: return mPWER; + case PCFR: return mPCFR; + case PPCR: return mPPCR; + case PGSR: return mPGSR; + case POSR: return mPOSR; + + default: + return 0; + } +} + +void PowerManager::put_data(uint32_t const address, uint32_t const value) +{ + switch (address) { + case PMCR: mPMCR = value; break; + case PSSR: mPSSR = value; break; + case PSPR: mPSPR = value; break; + case PWER: mPWER = value; break; + case PCFR: mPCFR = value; break; + + case PPCR: + // :SA-1110 Developer's Manual: Wei 2004-Jan-11: + // + // The PPCR contains bits used to configure the core operating frequency generated by the PLL. + // + // :NOTE: Wei 2004-Jan-11: + // + // However, I enforce the core operating frequency as 206 MHz. + // Thus, this register is no use to me. + mPPCR = value; + break; + + case PGSR: mPGSR = value; break; + case POSR: break; + default: break; + } +} + +} // namespace SA1100 diff --git a/WindCore/sa1100/power_manager.h b/WindCore/sa1100/power_manager.h new file mode 100644 index 0000000..000e0ae --- /dev/null +++ b/WindCore/sa1100/power_manager.h @@ -0,0 +1,81 @@ +// 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 POWER_MANAGER_H +#define POWER_MANAGER_H + +#include + +namespace SA1100 { + +class PowerManager { + private: + + enum { + PMCR = 0x90020000, + PSSR = 0x90020004, + PSPR = 0x90020008, + PWER = 0x9002000C, + PCFR = 0x90020010, + PPCR = 0x90020014, + PGSR = 0x90020018, + POSR = 0x9002001C + }; + + enum { + POSR_OOK = (1 << 0) + }; + + // Attribute + + uint32_t mPMCR; // Power manager control register + uint32_t mPSSR; // Power manager sleep status register + uint32_t mPSPR; // Power manager scratch pad register + uint32_t mPWER; // Power manager wake-up enable register; + uint32_t mPCFR; // Power manager general configuration register + uint32_t mPPCR; // Power manager PLL configuration register + uint32_t mPGSR; // Power manager GPIO sleep state register + uint32_t mPOSR; // Power manager oscillator status register + + // Operation + + void init_register() { + mPMCR = 0; + mPSSR = 0; + mPSPR = 0; + mPWER = 0; + mPCFR = 0; + mPPCR = 0; + mPGSR = 0; + mPOSR = 0; + } + + public: + + PowerManager() { + init_register(); + } + + void reset(); + + uint32_t get_data(uint32_t const address) const; + void put_data(uint32_t const address, uint32_t const value); +}; + +} // namespace SA1100 + +#endif // POWER_MANAGER_H diff --git a/WindCore/sa1100/reset_controller.cpp b/WindCore/sa1100/reset_controller.cpp new file mode 100644 index 0000000..4e84aec --- /dev/null +++ b/WindCore/sa1100/reset_controller.cpp @@ -0,0 +1,48 @@ +// 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 "reset_controller.h" + +namespace SA1100 +{ + + uint32_t + ResetController::get_data(uint32_t const address) const + { + switch (address) { + case RSRR: + // :SA-1110 Developer's Manual: Wei 2003-Dec-11: + // + // RSRR is write-only. + return 0; + + case RCSR: + return mRCSR; + + default: + return 0; + } + } + + void ResetController::put_data(uint32_t const address, uint32_t const value) { + switch (address) { + case RSRR: mRSRR = value; break; + case RCSR: mRCSR = value; break; + default: break; + } + } +} diff --git a/WindCore/sa1100/reset_controller.h b/WindCore/sa1100/reset_controller.h new file mode 100644 index 0000000..e4aed16 --- /dev/null +++ b/WindCore/sa1100/reset_controller.h @@ -0,0 +1,64 @@ +// 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 RESET_CONTROLLER_H +#define RESET_CONTROLLER_H + +#include + +namespace SA1100 +{ + +class ResetController { + private: + + enum { + RSRR = 0x90030000, + RCSR = 0x90030004 + }; + + uint32_t mRSRR; // Reset controller software reset register + uint32_t mRCSR; // Reset controller status register + + void init_register() { + mRSRR = 0; + mRCSR = 1; + } + + public: + + ResetController() { + init_register(); + } + + void reset() { + mRSRR = 0; + mRCSR = 2; + } + + bool resetRequested() { + return mRSRR & 0xFFFFFFFF; + } + + uint32_t get_data(uint32_t const address) const; + + void put_data(uint32_t const address, uint32_t const value); + }; + +} + +#endif // RESET_CONTROLLER_H diff --git a/WindCore/sa1100/serial_3.h b/WindCore/sa1100/serial_3.h new file mode 100644 index 0000000..d25d5d1 --- /dev/null +++ b/WindCore/sa1100/serial_3.h @@ -0,0 +1,54 @@ +// 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 . +// + +// Serial port 3 +// + +#ifndef SERIAL_3_H +#define SERIAL_3_H + +#include + +#include "uart.h" + +namespace SA1100 +{ +class Serial3 { + private: + + Uart m_UART; + + public: + // Access + + Uart const & get_UART() const { return m_UART; }; + + inline void reset() { m_UART.reset(); } + + inline void run() { m_UART.run(); } + + uint32_t get_data(uint32_t const address) { + return m_UART.get_data(address); + } + + void put_data(uint32_t const address, uint32_t const value) { + m_UART.put_data(address, value); + } + }; +} + +#endif // SERIAL_3_H diff --git a/WindCore/sa1100/uart.cpp b/WindCore/sa1100/uart.cpp new file mode 100644 index 0000000..66ab4b4 --- /dev/null +++ b/WindCore/sa1100/uart.cpp @@ -0,0 +1,322 @@ +// 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 "uart.h" + +namespace SA1100 +{ + inline void Uart::tx_data() { + // :NOTE: Wei 2004-Jun-10: + // + // Becuase of using fifo[0], fifo has to reside in a consecutive memory blocks. + // This is why I use std::vector rather than std::deque for m_tx_fifo. + // reinterpret_cast(mp_output_device)->put_char_to_serial_console(m_tx_fifo); + } + + inline void Uart::rx_data() { + // :TODO: Wei 2004-Jun-29: + // + // Receives data from serial console + // reinterpret_cast(mp_output_device)->get_char_from_serial_console(); + } + + Uart::Uart() { + init_register(); + } + + void Uart::run() { + if ((m_UTCR3 & UTCR3_TXE) && (m_tx_fifo.size() != 0)) + { + tx_data(); + + m_tx_fifo.clear(); + + m_UTSR0 |= UTSR0_TFS; // Enable Transmit FIFO service request + m_UTSR1 |= UTSR1_TNF; // Transmit FIFO not full + } + + if (m_UTCR3 & UTCR3_RXE) + { + // :NOTE: Wei 2004-Jul-1: + // + // Try to get more data. + rx_data(); + + switch (m_rx_fifo.size()) + { + case 0: + m_UTSR1 &= ~UTSR1_RNE; + break; + + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + // :SA-1110 Developer's Manual: p.338: Wei 2003-Jun-29: + // + // If receiver is enabled, receive FIFO not empty, 3 frame times elapsed without receiving data, request interrupt. + // + // :NOTE: Wei 2004-Jun-29: + // + // However, I don't see the reasone that I should implement a time-critical operation like this (3 frame times), + // thus, whenever the rx fifo isn't empty, ARMware will set the RID bit in UTSR0 up. + m_UTSR0 |= UTSR0_RID; + + m_UTSR1 |= UTSR1_RNE; + break; + + case 8: + case 9: + case 10: + case 11: + case 12: + default: + // :SA-1110 Developer's Manual: p.338: Wei 2003-Jun-29: + // + // Receive FIFO is one- to two-thirds full (contains 5, 6, 7, or 8 entries of data) or more, + // and receiver operation is enabled, DMA service request signalled, + // and interrupt request signalled if not masked (if RIE=1). + // + // :NOTE: Wei 2004-Jun-29: + // + // And I choice 8 elements as the delimiter. + m_UTSR0 |= (UTSR0_RFS | UTSR0_RID); + + m_UTSR1 |= UTSR1_RNE; + break; + } + } + } + + void Uart::reset() + { + // bit 7 sets 0 on reset. + m_UTCR0 &= ~(1 << 7); + + // bit 4, 5, 6, 7 set 0 on reset. + m_UTCR1 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4)); + + // bit 0, 1, 6, 7 set 0 on reset. + m_UTCR3 &= ~((1 << 7) | (1 << 6) | (1 << 1) | (1 << 0)); + + // bit 0, 1, 5, 6, 7 set 0 on reset. + m_UTSR0 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 1) | (1 << 0)); + + // bit 0, 1, 5, 6, 7 set 0 on reset. + m_UTSR1 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 1) | (1 << 0)); + } + + uint32_t Uart::get_data(uint32_t const address) + { + switch (address) + { + case UTCR0: + return static_cast(m_UTCR0); + + case UTCR1: + return static_cast(m_UTCR1); + + case UTCR2: + return static_cast(m_UTCR2); + + case UTCR3: + return static_cast(m_UTCR3); + + case UTDR: + { + if (true == m_rx_fifo.empty()) + { + assert(0 == (m_UTSR1 & UTSR1_RNE)); + return static_cast(0); + } + else + { + assert(UTSR1_RNE == (m_UTSR1 & UTSR1_RNE)); + uint8_t const ch = m_rx_fifo.front(); + + m_rx_fifo.pop_front(); + + switch (m_rx_fifo.size()) + { + case 0: + m_UTSR0 &= ~(UTSR0_RID | UTSR0_RFS); + + m_UTSR1 &= ~UTSR1_RNE; + break; + + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + m_UTSR0 &= ~UTSR0_RFS; + break; + + case 8: + case 9: + case 10: + case 11: + case 12: + default: + break; + } + return static_cast(ch); + } + } + + case UTSR0: + return static_cast(m_UTSR0); + + case UTSR1: + // :NOTE: Wei 2004-Jan-03: + // + // Bit 6, 7 are reserved. + // + // However, this is a read-only register, thus I don't have to do the mask operation. + return static_cast(m_UTSR1); + + default: + assert(!"Should not reach here."); + return 0; + } + } + + void Uart::put_data(uint32_t const address, uint32_t const value) { + switch (address) + { + case UTCR0: + // :NOTE: Wei 2004-Jan-03: + // + // Bit 7 is reserved. + m_UTCR0 = static_cast(value & ~(1 << 7)); + break; + + case UTCR1: + // :NOTE: Wei 2004-Jan-03: + // + // Bit 4, 5, 6, 7 are reserved. + m_UTCR1 = static_cast(value & ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4))); + break; + + case UTCR2: + m_UTCR2 = static_cast(value); + break; + + case UTCR3: + { + uint8_t const diff = (m_UTCR3 ^ value); + + if ((diff & UTCR3_TXE) && (0 == (value & UTCR3_TXE))) + { + if (m_tx_fifo.size() != 0) + { + // :SA-1110 Developer's Manual: p.332: Wei 2003-Jun-29: + // + // If the TXE bit is cleared to zero, all entries within the transmit FIFO are reset. + m_tx_fifo.clear(); + + m_UTSR0 |= UTSR0_TFS; // Enable Transmit FIFO service request + m_UTSR1 |= UTSR1_TNF; // Transmit FIFO not full + } + } + + if ((diff & UTCR3_RXE) && (0 == (value & UTCR3_RXE))) + { + // :SA-1110 Developer's Manual: p.332: Wei 2003-Jun-29: + // + // If the RXE bit is cleared to zero, all entries within the receive FIFO are reset + if (false == m_rx_fifo.empty()) + { + m_rx_fifo.clear(); + + m_UTSR0 &= ~(UTSR0_RID | UTSR0_RFS); + m_UTSR1 &= ~UTSR1_RNE; + } + } + + // :NOTE: Wei 2004-Jan-03: + // + // Bit 6, 7 are reserved. + m_UTCR3 = static_cast(value & ~((1 << 7) | (1 << 6))); + } + break; + + case UTDR: + switch (m_tx_fifo.size()) + { + case 0: + case 1: + case 2: + case 3: + break; + + case 4: + m_UTSR0 &= ~UTSR0_TFS; // disable 'Transmit FIFO service request' + break; + + case 5: + case 6: + break; + + case 7: + // :NOTE: Wei 2005-May-29: + // + // Because the ARMware has unlimited UART buffer, + // thus I will never rise down the TNF (Transmit FIFO Not Full) bit. + // + // m_UTSR1 &= ~(UTSR1_TNF); + break; + + case 8: + default: + break; + } + + m_tx_fifo.push_back(static_cast(value & 0xFF)); + break; + + case UTSR0: + // :SA-1110 Developer's Manual: p.336: Wei 2003-Jun-07: + // + // Writing a one to a sticky status bit clears it; writing a zero has no effect. + // Read-only flags are set and cleared by hardware; writes have no effect. + + // :NOTE: Wei 2004-Jan-03: + // + // Bit 6, 7 are reserved. + // bits 0, 1, 5 are read-only. + m_UTSR0 &= ~(value & UTSR0_READ_WRITE_BITS); + break; + + case UTSR1: + // :NOTE: Wei 2004-Jan-01: + // + // read-only register. + assert(!"Should not reach here."); + return; + + default: + assert(!"Should not reach here."); + break; + } + } +} diff --git a/WindCore/sa1100/uart.h b/WindCore/sa1100/uart.h new file mode 100644 index 0000000..8a83cd8 --- /dev/null +++ b/WindCore/sa1100/uart.h @@ -0,0 +1,153 @@ +// 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 UART_H +#define UART_H + +#include +#include +#include + +#include + +namespace SA1100 +{ + +class Uart { + public: + enum + { + UTCR3_RXE = (1 << 0), // Receiver enable. + UTCR3_TXE = (1 << 1), // Transmitter enable. + UTCR3_BRK = (1 << 2), // Break. + UTCR3_RIE = (1 << 3), // Receive FIFO interrupt enable. + UTCR3_TIE = (1 << 4), // Transmit FIFO interrupt enable. + UTCR3_LBM = (1 << 5) // Loopback mode. + }; + + enum + { + UTSR0_TFS = (1 << 0), // Transmit FIFO service request + UTSR0_RFS = (1 << 1), // Receive FIFO service request + UTSR0_RID = (1 << 2), // Receiver idle. + UTSR0_RBB = (1 << 3), // Receiver begin of break. + UTSR0_REB = (1 << 4), // Receiver end of break. + UTSR0_EIF = (1 << 5), // Error in FIFO (read-only). + + UTSR0_READ_WRITE_BITS = (UTSR0_RID | UTSR0_RBB | UTSR0_REB), + }; + + enum + { + UTSR1_TBY = (1 << 0), // Transmitter busy flag + UTSR1_RNE = (1 << 1), // Receive FIFO not empty + UTSR1_TNF = (1 << 2), // Transmit FIFO not full + UTSR1_PRE = (1 << 3), // Parity error + UTSR1_FRE = (1 << 4), // Framing error + UTSR1_ROR = (1 << 5) // Receive FIFO overrun + }; + + static uint32_t const PORT_NUMBER = 3; + + enum + { + UTCR0 = 0x80050000, + UTCR1 = 0x80050004, + UTCR2 = 0x80050008, + UTCR3 = 0x8005000C, + UTDR = 0x80050014, + UTSR0 = 0x8005001C, + UTSR1 = 0x80050020 + }; + + static uint32_t const RX_FIFO_SIZE = 12; + static uint32_t const TX_FIFO_SIZE = 8; + + private: + + // Attribute + + uint8_t m_UTCR0; // UART control register 0 + uint8_t m_UTCR1; // UART control register 1 + uint8_t m_UTCR2; // UART control register 2 + uint8_t m_UTCR3; // UART control register 3 + uint8_t m_UTDR; // UART data register + uint8_t m_UTSR0; // UART status register 0 + uint8_t m_UTSR1; // UART status register 1 + + // :NOTE: Wei 2005-May-29: + // + // SerialConsole needs m_tx_fifo be in a continus memory space, + // thus I use std::vector here. + std::vector m_tx_fifo; + std::deque m_rx_fifo; + + // :NOTE: Wei 2004-Mar-22: + // + // For now, this mp_output_device is only used in Uart to output to SerialConsole. + void *mp_output_device; + + // Operation + + void init_register() { + m_UTCR0 = 0; + m_UTCR1 = 0; + m_UTCR2 = 0; + m_UTCR3 = 0; + m_UTDR = 0; + + m_UTSR0 = UTSR0_TFS; // Transmit FIFO service request + m_UTSR1 = UTSR1_TNF; // Transmit FIFO not full + } + + void tx_data(); + void rx_data(); + + public: + Uart(); + void reset(); + + inline void register_output_device(void * const output_device) { + mp_output_device = output_device; + } + + void run(); + + inline uint32_t get_interrupt_status() const { + return m_UTSR0; + } + + inline uint32_t get_ctrl_reg_3() const { + return m_UTCR3; + } + + // :SA-1110 Developer's Manual: Wei 2003-Dec-14: + // + // All registers in the Peripheral Control Module are accessed via + // the CPU must be performed using word reads and writes. + uint32_t get_data(uint32_t const address); + + void put_data(uint32_t const address, uint32_t const value); + + inline std::deque& rx_fifo() { + return m_rx_fifo; + } +}; + +} // namespace SA1100 + +#endif // UART_H -- 2.45.2