dev: Use BitUnions and a RegisterBank in the Uart8250.
authorGabe Black <gabe.black@gmail.com>
Fri, 30 Oct 2020 11:03:01 +0000 (04:03 -0700)
committerGabe Black <gabe.black@gmail.com>
Fri, 18 Dec 2020 00:30:55 +0000 (00:30 +0000)
Change-Id: I139db4f08f9e6addfed4906ea6c49ee67439d30e
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/36818
Reviewed-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Maintainer: Giacomo Travaglini <giacomo.travaglini@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
src/dev/serial/uart8250.cc
src/dev/serial/uart8250.hh

index 9f93024a40a58b26922078bd3a029525074218c9..a6e9bd1509d96542078d851f78c8dce4c0429804 100644 (file)
@@ -47,14 +47,14 @@ using namespace std;
 void
 Uart8250::processIntrEvent(int intrBit)
 {
-    if (intrBit & IER) {
+    if (intrBit & registers.ier.get()) {
        DPRINTF(Uart, "UART InterEvent, interrupting\n");
        platform->postConsoleInt();
        status |= intrBit;
        lastTxInt = curTick();
-    }
-    else
+    } else {
        DPRINTF(Uart, "UART InterEvent, not interrupting\n");
+    }
 
 }
 
@@ -84,89 +84,141 @@ Uart8250::scheduleIntr(Event *event)
 
 
 Uart8250::Uart8250(const Params &p)
-    : Uart(p, 8), IER(0), LCR(0), MCR(0), lastTxInt(0),
+    : Uart(p, 8), registers(this, name() + ".registers"), lastTxInt(0),
       txIntrEvent([this]{ processIntrEvent(TX_INT); }, "TX"),
       rxIntrEvent([this]{ processIntrEvent(RX_INT); }, "RX")
 {
 }
 
-Tick
-Uart8250::read(PacketPtr pkt)
+Uart8250::Registers::Registers(Uart8250 *uart, const std::string &new_name) :
+    RegisterBankLE(new_name, 0), rbrThr(rbr, thr), rbrThrDll(rbrThr, dll),
+    ierDlh(ier, dlh), iirFcr(iir, fcr)
 {
-    assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize);
-    assert(pkt->getSize() == 1);
+    rbr.reader(uart, &Uart8250::readRbr);
+    thr.writer(uart, &Uart8250::writeThr);
+    ier.writer(uart, &Uart8250::writeIer);
+    iir.reader(uart, &Uart8250::readIir);
 
-    Addr daddr = pkt->getAddr() - pioAddr;
+    lcr.writer([this](auto &reg, const auto &value) {
+            reg.update(value);
+            rbrThrDll.select(value.dlab);
+            ierDlh.select(value.dlab);
+        });
 
-    DPRINTF(Uart, " read register %#x\n", daddr);
-
-    switch (daddr) {
-        case 0x0:
-            if (!(LCR & 0x80)) { // read byte
-                if (device->dataAvailable())
-                    pkt->setRaw(device->readData());
-                else {
-                    pkt->setRaw((uint8_t)0);
-                    // A limited amount of these are ok.
-                    DPRINTF(Uart, "empty read of RX register\n");
-                }
-                status &= ~RX_INT;
-                platform->clearConsoleInt();
-
-                if (device->dataAvailable() && (IER & UART_IER_RDI))
-                    scheduleIntr(&rxIntrEvent);
-            } else { // dll divisor latch
-               ;
-            }
-            break;
-        case 0x1:
-            if (!(LCR & 0x80)) { // Intr Enable Register(IER)
-                pkt->setRaw(IER);
-            } else { // DLM divisor latch MSB
-                ;
-            }
-            break;
-        case 0x2: // Intr Identification Register (IIR)
-            DPRINTF(Uart, "IIR Read, status = %#x\n", (uint32_t)status);
-
-            if (status & RX_INT) /* Rx data interrupt has a higher priority */
-                pkt->setRaw(IIR_RXID);
-            else if (status & TX_INT) {
-                pkt->setRaw(IIR_TXID);
-                //Tx interrupts are cleared on IIR reads
-                status &= ~TX_INT;
-            } else
-                pkt->setRaw(IIR_NOPEND);
-
-            break;
-        case 0x3: // Line Control Register (LCR)
-            pkt->setRaw(LCR);
-            break;
-        case 0x4: // Modem Control Register (MCR)
-            pkt->setRaw(MCR);
-            break;
-        case 0x5: // Line Status Register (LSR)
-            uint8_t lsr;
-            lsr = 0;
-            // check if there are any bytes to be read
+    mcr.writer([](auto &reg, const auto &value) {
+            if (value == (UART_MCR_LOOP | 0x0A))
+                reg.update(0x9A);
+        });
+
+    lsr.readonly().
+        reader([device = uart->device](auto &reg) {
+            Lsr lsr = 0;
             if (device->dataAvailable())
-                lsr = UART_LSR_DR;
-            lsr |= UART_LSR_TEMT | UART_LSR_THRE;
-            pkt->setRaw(lsr);
-            break;
-        case 0x6: // Modem Status Register (MSR)
-            pkt->setRaw((uint8_t)0);
-            break;
-        case 0x7: // Scratch Register (SCR)
-            pkt->setRaw((uint8_t)0); // doesn't exist with at 8250.
-            break;
-        default:
-            panic("Tried to access a UART port that doesn't exist\n");
-            break;
+                lsr.rdr = 1;
+            lsr.tbe = 1;
+            lsr.txEmpty = 1;
+            return lsr;
+        });
+
+    msr.readonly();
+
+    addRegisters({rbrThrDll, ierDlh, iirFcr, lcr, mcr, lsr, msr, sr});
+}
+
+uint8_t
+Uart8250::readRbr(Register8 &reg)
+{
+    uint8_t data = 0;
+    if (device->dataAvailable())
+        data = device->readData();
+    else
+        DPRINTF(Uart, "empty read of RX register\n");
+
+    status &= ~RX_INT;
+    platform->clearConsoleInt();
+
+    if (device->dataAvailable() && registers.ier.get().rdi)
+        scheduleIntr(&rxIntrEvent);
+
+    return data;
+}
+
+void
+Uart8250::writeThr(Register8 &reg, const uint8_t &data)
+{
+    device->writeData(data);
+    platform->clearConsoleInt();
+    status &= ~TX_INT;
+    if (registers.ier.get().thri)
+        scheduleIntr(&txIntrEvent);
+}
+
+Uart8250::Iir
+Uart8250::readIir(Register<Iir> &reg)
+{
+    DPRINTF(Uart, "IIR Read, status = %#x\n", (uint32_t)status);
+
+    Iir iir = 0;
+    if (status & RX_INT) {
+        // Rx data interrupt has a higher priority.
+        iir.id = (uint8_t)InterruptIds::Rx;
+    } else if (status & TX_INT) {
+        iir.id = (uint8_t)InterruptIds::Tx;
+        // Tx interrupts are cleared on IIR reads.
+        status &= ~TX_INT;
+    } else {
+        iir.pending = 1;
     }
-/*    uint32_t d32 = *data;
-    DPRINTF(Uart, "Register read to register %#x returned %#x\n", daddr, d32);
-*/
+    return iir;
+}
+
+void
+Uart8250::writeIer(Register<Ier> &reg, const Ier &ier)
+{
+    reg.update(ier);
+
+    if (ier.thri) {
+        DPRINTF(Uart, "IER: IER_THRI set, scheduling TX intrrupt\n");
+        if (curTick() - lastTxInt > 225 * SimClock::Int::ns) {
+            DPRINTF(Uart, "-- Interrupting Immediately... %d,%d\n",
+                    curTick(), lastTxInt);
+            txIntrEvent.process();
+        } else {
+            DPRINTF(Uart, "-- Delaying interrupt... %d,%d\n",
+                    curTick(), lastTxInt);
+            scheduleIntr(&txIntrEvent);
+        }
+    } else {
+        DPRINTF(Uart, "IER: IER_THRI cleared, descheduling TX intrrupt\n");
+        if (txIntrEvent.scheduled())
+            deschedule(txIntrEvent);
+        if (status & TX_INT)
+            platform->clearConsoleInt();
+        status &= ~TX_INT;
+    }
+
+    if (ier.rdi && device->dataAvailable()) {
+        DPRINTF(Uart, "IER: IER_RDI set, scheduling RX intrrupt\n");
+        scheduleIntr(&rxIntrEvent);
+    } else {
+        DPRINTF(Uart, "IER: IER_RDI cleared, descheduling RX intrrupt\n");
+        if (rxIntrEvent.scheduled())
+            deschedule(rxIntrEvent);
+        if (status & RX_INT)
+            platform->clearConsoleInt();
+        status &= ~RX_INT;
+    }
+}
+
+Tick
+Uart8250::read(PacketPtr pkt)
+{
+    Addr daddr = pkt->getAddr() - pioAddr;
+
+    DPRINTF(Uart, "Read register %#x\n", daddr);
+
+    registers.read(daddr, pkt->getPtr<void>(), pkt->getSize());
+
     pkt->makeAtomicResponse();
     return pioDelay;
 }
@@ -174,88 +226,13 @@ Uart8250::read(PacketPtr pkt)
 Tick
 Uart8250::write(PacketPtr pkt)
 {
-
-    assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize);
-    assert(pkt->getSize() == 1);
-
     Addr daddr = pkt->getAddr() - pioAddr;
 
-    DPRINTF(Uart, " write register %#x value %#x\n", daddr,
+    DPRINTF(Uart, "Write register %#x value %#x\n", daddr,
             pkt->getRaw<uint8_t>());
 
-    switch (daddr) {
-        case 0x0:
-            if (!(LCR & 0x80)) { // write byte
-                device->writeData(pkt->getRaw<uint8_t>());
-                platform->clearConsoleInt();
-                status &= ~TX_INT;
-                if (UART_IER_THRI & IER)
-                    scheduleIntr(&txIntrEvent);
-            } else { // dll divisor latch
-               ;
-            }
-            break;
-        case 0x1:
-            if (!(LCR & 0x80)) { // Intr Enable Register(IER)
-                IER = pkt->getRaw<uint8_t>();
-                if (UART_IER_THRI & IER)
-                {
-                    DPRINTF(Uart,
-                            "IER: IER_THRI set, scheduling TX intrrupt\n");
-                    if (curTick() - lastTxInt > 225 * SimClock::Int::ns) {
-                        DPRINTF(Uart, "-- Interrupting Immediately... %d,%d\n",
-                                curTick(), lastTxInt);
-                        txIntrEvent.process();
-                    } else {
-                        DPRINTF(Uart, "-- Delaying interrupt... %d,%d\n",
-                                curTick(), lastTxInt);
-                        scheduleIntr(&txIntrEvent);
-                    }
-                }
-                else
-                {
-                    DPRINTF(Uart, "IER: IER_THRI cleared, "
-                            "descheduling TX intrrupt\n");
-                    if (txIntrEvent.scheduled())
-                        deschedule(txIntrEvent);
-                    if (status & TX_INT)
-                        platform->clearConsoleInt();
-                    status &= ~TX_INT;
-                }
-
-                if ((UART_IER_RDI & IER) && device->dataAvailable()) {
-                    DPRINTF(Uart,
-                            "IER: IER_RDI set, scheduling RX intrrupt\n");
-                    scheduleIntr(&rxIntrEvent);
-                } else {
-                    DPRINTF(Uart, "IER: IER_RDI cleared, "
-                            "descheduling RX intrrupt\n");
-                    if (rxIntrEvent.scheduled())
-                        deschedule(rxIntrEvent);
-                    if (status & RX_INT)
-                        platform->clearConsoleInt();
-                    status &= ~RX_INT;
-                }
-             } else { // DLM divisor latch MSB
-                ;
-            }
-            break;
-        case 0x2: // FIFO Control Register (FCR)
-            break;
-        case 0x3: // Line Control Register (LCR)
-            LCR = pkt->getRaw<uint8_t>();
-            break;
-        case 0x4: // Modem Control Register (MCR)
-            if (pkt->getRaw<uint8_t>() == (UART_MCR_LOOP | 0x0A))
-                    MCR = 0x9A;
-            break;
-        case 0x7: // Scratch Register (SCR)
-            // We are emulating a 8250 so we don't have a scratch reg
-            break;
-        default:
-            panic("Tried to access a UART port that doesn't exist\n");
-            break;
-    }
+    registers.write(daddr, pkt->getPtr<void>(), pkt->getSize());
+
     pkt->makeAtomicResponse();
     return pioDelay;
 }
@@ -263,9 +240,8 @@ Uart8250::write(PacketPtr pkt)
 void
 Uart8250::dataAvailable()
 {
-    // if the kernel wants an interrupt when we have data
-    if (IER & UART_IER_RDI)
-    {
+    // If the kernel wants an interrupt when we have data.
+    if (registers.ier.get().rdi) {
         platform->postConsoleInt();
         status |= RX_INT;
     }
@@ -284,9 +260,9 @@ void
 Uart8250::serialize(CheckpointOut &cp) const
 {
     SERIALIZE_SCALAR(status);
-    SERIALIZE_SCALAR(IER);
-    SERIALIZE_SCALAR(LCR);
-    SERIALIZE_SCALAR(MCR);
+    paramOut(cp, "IER", registers.ier);
+    paramOut(cp, "LCR", registers.lcr);
+    paramOut(cp, "MCR", registers.mcr);
     Tick rxintrwhen;
     if (rxIntrEvent.scheduled())
         rxintrwhen = rxIntrEvent.when();
@@ -305,9 +281,9 @@ void
 Uart8250::unserialize(CheckpointIn &cp)
 {
     UNSERIALIZE_SCALAR(status);
-    UNSERIALIZE_SCALAR(IER);
-    UNSERIALIZE_SCALAR(LCR);
-    UNSERIALIZE_SCALAR(MCR);
+    paramIn(cp, "IER", registers.ier);
+    paramIn(cp, "LCR", registers.lcr);
+    paramIn(cp, "MCR", registers.mcr);
     Tick rxintrwhen;
     Tick txintrwhen;
     UNSERIALIZE_SCALAR(rxintrwhen);
index b18079d57cae2919ba452ed7796616802c794f54..061676f2965b943d130717ae01ba6f036e34d0ad 100644 (file)
 #ifndef __DEV_UART8250_HH__
 #define __DEV_UART8250_HH__
 
+#include "base/bitunion.hh"
+#include "base/logging.hh"
 #include "dev/io_device.hh"
+#include "dev/reg_bank.hh"
 #include "dev/serial/uart.hh"
 #include "params/Uart8250.hh"
 
-/* UART8250 Interrupt ID Register
- *  bit 0    Interrupt Pending 0 = true, 1 = false
- *  bit 2:1  ID of highest priority interrupt
- *  bit 7:3  zeroes
- */
-const uint8_t IIR_NOPEND = 0x1;
-
-// Interrupt IDs
-const uint8_t IIR_MODEM = 0x00; /* Modem Status (lowest priority) */
-const uint8_t IIR_TXID  = 0x02; /* Tx Data */
-const uint8_t IIR_RXID  = 0x04; /* Rx Data */
-const uint8_t IIR_LINE  = 0x06; /* Rx Line Status (highest priority)*/
-
-const uint8_t UART_IER_RDI  = 0x01;
-const uint8_t UART_IER_THRI = 0x02;
-const uint8_t UART_IER_RLSI = 0x04;
-
-
-const uint8_t UART_LSR_TEMT = 0x40;
-const uint8_t UART_LSR_THRE = 0x20;
-const uint8_t UART_LSR_DR   = 0x01;
-
 const uint8_t UART_MCR_LOOP = 0x10;
 
-
 class Terminal;
 class Platform;
 
 class Uart8250 : public Uart
 {
   protected:
-    uint8_t IER, LCR, MCR;
+    BitUnion8(Ier)
+        Bitfield<0> rdi; // Receive data available interrupt.
+        Bitfield<1> thri; // Transmit holding register interrupt.
+        Bitfield<2> rlsi; // Receive line status interrupt.
+        Bitfield<3> msi; // Modem status interrupt.
+    EndBitUnion(Ier)
+
+    BitUnion8(Iir)
+        Bitfield<0> pending; // 0 = pending, 1 = not pending.
+        Bitfield<2, 1> id; // ID of highest priority interrupt.
+        Bitfield<7, 3> zeroes;
+    EndBitUnion(Iir)
+
+    BitUnion8(Lcr)
+        Bitfield<1, 0> wordSize;
+        Bitfield<2> stopBits;
+        Bitfield<5, 3> parity;
+        Bitfield<6> breakCont;
+        Bitfield<7> dlab;
+    EndBitUnion(Lcr)
+
+    BitUnion8(Lsr)
+        Bitfield<0> rdr; // Received data ready?
+        Bitfield<1> overrunError;
+        Bitfield<2> parityError;
+        Bitfield<3> framingError;
+        Bitfield<4> breakCond;
+        Bitfield<5> tbe; // Transmit buffer empty.
+        Bitfield<6> txEmpty; // Transmitter empty.
+        Bitfield<7> unused;
+    EndBitUnion(Lsr)
+
+    enum class InterruptIds {
+        Modem = 0, // Modem Status (lowest priority).
+        Tx = 1,    // Tx Data.
+        Rx = 2,    // Rx Data.
+        Line = 3,  // Rx Line Status (highest priority).
+    };
+
+    class Registers : public RegisterBankLE
+    {
+      public:
+        Registers(Uart8250 *uart, const std::string &new_name);
+
+        class PairedRegister : public RegisterBase
+        {
+          protected:
+            RegisterBase &_reg1, &_reg2;
+
+          public:
+            PairedRegister(RegisterBase &reg1, RegisterBase &reg2) :
+                RegisterBase(reg1.name() + "/" + reg2.name(), reg1.size()),
+                _reg1(reg1), _reg2(reg2)
+            {
+                panic_if(reg1.size() != reg2.size(),
+                        "Mismatched paired register sizes %d, %d",
+                        reg1.size(), reg2.size());
+            }
+
+            void serialize(std::ostream &os) const override {}
+            bool unserialize(const std::string &s) override { return true; }
+        };
+
+        class BankedRegister : public PairedRegister
+        {
+          private:
+            RegisterBase *selected = nullptr;
+
+          public:
+            BankedRegister(RegisterBase &reg1, RegisterBase &reg2) :
+                PairedRegister(reg1, reg2), selected(&reg1)
+            {}
+
+            void select(bool second) { selected = second ? &_reg2 : &_reg1; }
+
+            const std::string &
+            name() const override
+            {
+                return selected->name();
+            }
+
+            void read(void *buf) override { selected->read(buf); }
+            void
+            read(void *buf, off_t offset, size_t bytes) override
+            {
+                selected->read(buf, offset, bytes);
+            }
+            void write(const void *buf) override { selected->write(buf); }
+            void
+            write(const void *buf, off_t offset, size_t bytes) override
+            {
+                selected->write(buf, offset, bytes);
+            }
+        };
+
+        class RWSwitchedRegister : public PairedRegister
+        {
+          public:
+            using PairedRegister::PairedRegister;
+
+            void read(void *buf) override { _reg1.read(buf); }
+            void
+            read(void *buf, off_t offset, size_t bytes) override
+            {
+                _reg1.read(buf, offset, bytes);
+            }
+            void write(const void *buf) override { _reg2.write(buf); }
+            void
+            write(const void *buf, off_t offset, size_t bytes) override
+            {
+                _reg2.write(buf, offset, bytes);
+            }
+        };
+
+        // Offset 0.
+        Register8 rbr = {"rbr"};
+        Register8 thr = {"thr"};
+        RWSwitchedRegister rbrThr;
+
+        Register8 dll = {"dll"};
+        BankedRegister rbrThrDll;
+
+        // Offset 1.
+        Register<Ier> ier = {"ier", 0};
+        Register8 dlh = {"dlh"};
+        BankedRegister ierDlh;
+
+        // Offset 2.
+        Register<Iir> iir = {"iir"};
+        Register8 fcr = {"fcr"};
+        RWSwitchedRegister iirFcr;
+
+        // Offsets 3 - 6.
+        Register<Lcr> lcr = {"lcr"};
+        Register8 mcr = {"mcr"};
+        Register<Lsr> lsr = {"lsr"};
+        Register8 msr = {"msr"};
+
+        // The scratch register didn't exist on the 8250.
+        RegisterRaz sr = {"sr", 1};
+    };
+    using Register8 = Registers::Register8;
+    template <class T>
+    using Register = Registers::Register<T>;
+
+    Registers registers;
+
+    uint8_t readRbr(Register8 &reg);
+    void writeThr(Register8 &reg, const uint8_t &data);
+    void writeIer(Register<Ier> &reg, const Ier &ier);
+    Iir readIir(Register<Iir> &reg);
+
     Tick lastTxInt;
 
     void processIntrEvent(int intrBit);