From b6df44ecdc3ee9c488390f0e40e31c9b37ea6e3a Mon Sep 17 00:00:00 2001 From: George Wright Date: Wed, 24 Jun 2026 19:38:10 -0700 Subject: [PATCH] sa1100: add initial uart implementation --- CMakeLists.txt | 1 + WindCore/series7/uart.cpp | 356 ++++++++++++++++++++++++++++++++++++++ WindCore/series7/uart.h | 115 ++++++++++++ 3 files changed, 472 insertions(+) create mode 100644 WindCore/series7/uart.cpp create mode 100644 WindCore/series7/uart.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5428ba9..586305b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ set(WIND_CORE_SOURCES WindCore/series7/rtc.cpp WindCore/series7/sa1100.cpp WindCore/series7/sa1100_memory.cpp + WindCore/series7/uart.cpp ) add_library(WindCore STATIC ${WIND_CORE_SOURCES}) diff --git a/WindCore/series7/uart.cpp b/WindCore/series7/uart.cpp new file mode 100644 index 0000000..2ef30e8 --- /dev/null +++ b/WindCore/series7/uart.cpp @@ -0,0 +1,356 @@ +// 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 { + +Uart::Uart(std::function& output) : mSink(output) { + initRegisters(); +} + +void Uart::initRegisters() { + mUTCR0 = 0; + mUTCR1 = 0; + mUTCR2 = 0; + mUTCR3 = 0; + mUTDR = 0; + + mUTSR0 = UTSR0_TFS; // Transmit FIFO service request + mUTSR1 = UTSR1_TNF; // Transmit FIFO not full + mTxFifo.clear(); + mRxFifo.clear(); + mNextTxCycle = 0; + mTxBuffer.clear(); +} + +void Uart::reset() { + // bit 7 sets 0 on reset. + mUTCR0 &= ~(1 << 7); + + // bit 4, 5, 6, 7 set 0 on reset. + mUTCR1 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4)); + + // bit 0, 1, 6, 7 set 0 on reset. + mUTCR3 &= ~((1 << 7) | (1 << 6) | (1 << 1) | (1 << 0)); + + // bit 0, 1, 5, 6, 7 set 0 on reset. + mUTSR0 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 1) | (1 << 0)); + + // bit 0, 1, 5, 6, 7 set 0 on reset. + mUTSR1 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 1) | (1 << 0)); + + mTxFifo.clear(); + mRxFifo.clear(); + mNextTxCycle = 0; + mTxBuffer.clear(); +} + +void Uart::run(int64_t passedCycles) { + if (mUTCR3 & UTCR3_TXE) { + if (!mTxFifo.empty()) { + if (mNextTxCycle == 0) { + mNextTxCycle = passedCycles + 16000; + } + while (!mTxFifo.empty() && passedCycles >= mNextTxCycle) { + uint8_t ch = mTxFifo.front(); + mTxFifo.erase(mTxFifo.begin()); + + if (ch & 0x80) { + ch = 0x20; + } + mTxBuffer.push_back(static_cast(ch)); + if (ch == '\n' || ch == '\r' || mTxBuffer.size() >= 120) { + flushTxBuffer(); + } + + if (mTxFifo.size() <= 4) { + mUTSR0 |= UTSR0_TFS; + } + if (mTxFifo.size() < 8) { + mUTSR1 |= UTSR1_TNF; + } + + if (!mTxFifo.empty()) { + mNextTxCycle += 16000; + if (mNextTxCycle <= passedCycles) { + mNextTxCycle = passedCycles + 16000; + } + } else { + mNextTxCycle = 0; + } + } + } + } + + if (mUTCR3 & UTCR3_RXE) { + switch (mRxFifo.size()) { + case 0: + mUTSR1 &= ~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. + mUTSR0 |= UTSR0_RID; + + mUTSR1 |= 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. + mUTSR0 |= (UTSR0_RFS | UTSR0_RID); + + mUTSR1 |= UTSR1_RNE; + break; + } + } +} + +inline void Uart::txData() { + // :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 mTxFifo. + std::string output; + + for (std::vector::iterator iter = mTxFifo.begin(); + iter != mTxFifo.end(); ++iter) { + if ((*iter) & 0x80) { + (*iter) = 0x20; + } + } + + if (0xd == mTxFifo.back()) { + output.append(reinterpret_cast(&(mTxFifo[0])), mTxFifo.size()); + mNeedAppendMore = true; + } else { + if (true == mNeedAppendMore) { + output.append(reinterpret_cast(&(mTxFifo[0])), + mTxFifo.size()); + mSink(output.c_str()); + mNeedAppendMore = false; + } else { + if (false == output.empty()) { + mSink(output.c_str()); + } + + std::string lala = std::string( + reinterpret_cast(&(mTxFifo[0])), mTxFifo.size()); + mSink(lala.c_str()); + } + } +} + +void Uart::flushTxBuffer() { + if (!mTxBuffer.empty()) { + mSink(mTxBuffer.c_str()); + mTxBuffer.clear(); + } +} + +uint32_t Uart::read32(const uint32_t address) { + switch (address) { + case UTCR0: + return static_cast(mUTCR0); + + case UTCR1: + return static_cast(mUTCR1); + + case UTCR2: + return static_cast(mUTCR2); + + case UTCR3: + return static_cast(mUTCR3); + + case UTDR: { + if (true == mRxFifo.empty()) { + assert(0 == (mUTSR1 & UTSR1_RNE)); + return static_cast(0); + } else { + assert(UTSR1_RNE == (mUTSR1 & UTSR1_RNE)); + uint8_t const ch = mRxFifo.front(); + + mRxFifo.pop_front(); + + switch (mRxFifo.size()) { + case 0: + mUTSR0 &= ~(UTSR0_RID | UTSR0_RFS); + + mUTSR1 &= ~UTSR1_RNE; + break; + + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + mUTSR0 &= ~UTSR0_RFS; + break; + + case 8: + case 9: + case 10: + case 11: + case 12: + default: + break; + } + return static_cast(ch); + } + } + + case UTSR0: + return static_cast(mUTSR0); + + 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(mUTSR1); + + default: + return 0; + } +} + +void Uart::write32(const uint32_t address, const uint32_t value) { + switch (address) { + case UTCR0: + // :NOTE: Wei 2004-Jan-03: + // + // Bit 7 is reserved. + mUTCR0 = static_cast(value & ~(1 << 7)); + break; + + case UTCR1: + // :NOTE: Wei 2004-Jan-03: + // + // Bit 4, 5, 6, 7 are reserved. + mUTCR1 = static_cast(value & + ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4))); + break; + + case UTCR2: + mUTCR2 = static_cast(value); + break; + + case UTCR3: { + uint8_t const diff = (mUTCR3 ^ value); + + if ((diff & UTCR3_TXE) && (0 == (value & UTCR3_TXE))) { + if (mTxFifo.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. + mTxFifo.clear(); + + mUTSR0 |= UTSR0_TFS; // Enable Transmit FIFO service request + mUTSR1 |= UTSR1_TNF; // Transmit FIFO not full + mNextTxCycle = 0; + mTxBuffer.clear(); + } + } + + 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 == mRxFifo.empty()) { + mRxFifo.clear(); + + mUTSR0 &= ~(UTSR0_RID | UTSR0_RFS); + mUTSR1 &= ~UTSR1_RNE; + } + } + + // :NOTE: Wei 2004-Jan-03: + // + // Bit 6, 7 are reserved. + mUTCR3 = static_cast(value & ~((1 << 7) | (1 << 6))); + } break; + + case UTDR: + mTxFifo.push_back(static_cast(value & 0xFF)); + if (mTxFifo.size() > 4) { + mUTSR0 &= ~UTSR0_TFS; // clear TFS (transmit FIFO is more than half full) + } + if (mTxFifo.size() >= 8) { + mUTSR1 &= ~UTSR1_TNF; // clear TNF (transmit FIFO is full) + } + 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. + mUTSR0 &= ~(value & UTSR0_READ_WRITE_BITS); + break; + + case UTSR1: + // :NOTE: Wei 2004-Jan-01: + // + // read-only register. + return; + + default: + break; + } +} +} // namespace SA1100 diff --git a/WindCore/series7/uart.h b/WindCore/series7/uart.h new file mode 100644 index 0000000..334fa97 --- /dev/null +++ b/WindCore/series7/uart.h @@ -0,0 +1,115 @@ +// 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 +#include +#include + +namespace SA1100 { + +class IntController; + +class Uart { +public: + Uart(std::function& output); + void reset(); + + void run(int64_t passedCycles); + void flushTxBuffer(); + + inline int64_t getNextTxEventCycle() const { + return (mUTCR3 & UTCR3_TXE) && !mTxFifo.empty() ? mNextTxCycle : 0; + } + + inline uint32_t getInterruptStatus() const { return mUTSR0; } + inline uint32_t getCtrlRegister() const { return mUTCR3; } + + uint32_t read32(const uint32_t address); + void write32(const uint32_t address, const uint32_t value); + +private: + void initRegisters(); + + void txData(); + void rxData(); + + 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 + }; + + enum { + UTCR0 = 0x80050000, + UTCR1 = 0x80050004, + UTCR2 = 0x80050008, + UTCR3 = 0x8005000C, + UTDR = 0x80050014, + UTSR0 = 0x8005001C, + UTSR1 = 0x80050020 + }; + + uint8_t mUTCR0; // UART control register 0 + uint8_t mUTCR1; // UART control register 1 + uint8_t mUTCR2; // UART control register 2 + uint8_t mUTCR3; // UART control register 3 + uint8_t mUTDR; // UART data register + uint8_t mUTSR0; // UART status register 0 + uint8_t mUTSR1; // UART status register 1 + + std::vector mTxFifo; + std::deque mRxFifo; + bool mNeedAppendMore = false; + std::function& mSink; + std::string mTxBuffer; + int64_t mNextTxCycle = 0; + + friend class IntController; +}; + +} // namespace SA1100 + +#endif // UART_H -- 2.45.2