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");
+ }
}
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<Iir> ®)
+{
+ 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> ®, 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;
}
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;
}
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;
}
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();
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);
#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 = {"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 ®);
+ void writeThr(Register8 ®, const uint8_t &data);
+ void writeIer(Register<Ier> ®, const Ier &ier);
+ Iir readIir(Register<Iir> ®);
+
Tick lastTxInt;
void processIntrEvent(int intrBit);