From 150fef84535dbcc24bde94643b769e449fc73b81 Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Fri, 30 Oct 2020 04:03:01 -0700 Subject: [PATCH] dev: Use BitUnions and a RegisterBank in the Uart8250. Change-Id: I139db4f08f9e6addfed4906ea6c49ee67439d30e Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/36818 Reviewed-by: Giacomo Travaglini Maintainer: Giacomo Travaglini Tested-by: kokoro --- src/dev/serial/uart8250.cc | 302 +++++++++++++++++-------------------- src/dev/serial/uart8250.hh | 179 +++++++++++++++++++--- 2 files changed, 294 insertions(+), 187 deletions(-) diff --git a/src/dev/serial/uart8250.cc b/src/dev/serial/uart8250.cc index 9f93024a4..a6e9bd150 100644 --- a/src/dev/serial/uart8250.cc +++ b/src/dev/serial/uart8250.cc @@ -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 ®, 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 ®, const auto &value) { + if (value == (UART_MCR_LOOP | 0x0A)) + reg.update(0x9A); + }); + + lsr.readonly(). + reader([device = uart->device](auto ®) { + 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 ®) +{ + 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 ®, const uint8_t &data) +{ + device->writeData(data); + platform->clearConsoleInt(); + status &= ~TX_INT; + if (registers.ier.get().thri) + scheduleIntr(&txIntrEvent); +} + +Uart8250::Iir +Uart8250::readIir(Register ®) +{ + 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 ®, 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(), 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()); - switch (daddr) { - case 0x0: - if (!(LCR & 0x80)) { // write byte - device->writeData(pkt->getRaw()); - 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(); - 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(); - break; - case 0x4: // Modem Control Register (MCR) - if (pkt->getRaw() == (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(), 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); diff --git a/src/dev/serial/uart8250.hh b/src/dev/serial/uart8250.hh index b18079d57..061676f29 100644 --- a/src/dev/serial/uart8250.hh +++ b/src/dev/serial/uart8250.hh @@ -33,42 +33,173 @@ #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 ®1, RegisterBase ®2) : + 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 ®1, RegisterBase ®2) : + PairedRegister(reg1, reg2), selected(®1) + {} + + 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", 0}; + Register8 dlh = {"dlh"}; + BankedRegister ierDlh; + + // Offset 2. + Register iir = {"iir"}; + Register8 fcr = {"fcr"}; + RWSwitchedRegister iirFcr; + + // Offsets 3 - 6. + Register lcr = {"lcr"}; + Register8 mcr = {"mcr"}; + Register lsr = {"lsr"}; + Register8 msr = {"msr"}; + + // The scratch register didn't exist on the 8250. + RegisterRaz sr = {"sr", 1}; + }; + using Register8 = Registers::Register8; + template + using Register = Registers::Register; + + Registers registers; + + uint8_t readRbr(Register8 ®); + void writeThr(Register8 ®, const uint8_t &data); + void writeIer(Register ®, const Ier &ier); + Iir readIir(Register ®); + Tick lastTxInt; void processIntrEvent(int intrBit); -- 2.30.2