Skip to content
Permalink
Browse files

Implement a minimal MMIO 8250 UART

I tested the block against coreboot's driver for memory-mapped
8250-compatible UARTs, but most features are missing:

 - Any writes to other registers than the first one are silently
   discarded. This includes the scratch register and the
   interrupt-enable register.
 - Non-byte sized accesses are not supported
 - The FIFO isn't emulated. Instead every written byte is immediately
   printed on stdout, and the line status register always indicates that
   new bytes can be sent.
 - Input doesn't work, either.
  • Loading branch information...
neuschaefer committed Jun 10, 2016
1 parent f5ecf65 commit 664118976cd487c9dec8cc6b5b3b9d52bd3f861c
Showing with 73 additions and 2 deletions.
  1. +1 −0 riscv/devices.cc
  2. +7 −0 riscv/devices.h
  3. +1 −0 riscv/riscv.mk.in
  4. +9 −2 riscv/sim.cc
  5. +1 −0 riscv/sim.h
  6. +54 −0 riscv/uart.cc
@@ -1,4 +1,5 @@
#include "devices.h"
#include <stdio.h>

void bus_t::add_device(reg_t addr, abstract_device_t* dev)
{
@@ -47,4 +47,11 @@ class rtc_t : public abstract_device_t {
uint64_t time() { return regs[0]; }
};

class uart_t : public abstract_device_t {
public:
bool load(reg_t addr, size_t len, uint8_t* bytes);
bool store(reg_t addr, size_t len, const uint8_t* bytes);
size_t size() { return 8; }
};

#endif
@@ -47,6 +47,7 @@ riscv_srcs = \
devices.cc \
rom.cc \
rtc.cc \
uart.cc \
gdbserver.cc \
debug_module.cc \
$(riscv_gen_srcs) \
@@ -51,6 +51,7 @@ sim_t::sim_t(const char* isa, size_t nprocs, size_t mem_mb, bool halted,
}

rtc.reset(new rtc_t(procs));
uart.reset(new uart_t());
make_config_string();
}

@@ -149,11 +150,14 @@ bool sim_t::mmio_store(reg_t addr, size_t len, const uint8_t* bytes)

void sim_t::make_config_string()
{
reg_t rtc_addr = EXT_IO_BASE;
reg_t rtc_addr = EXT_IO_BASE;
reg_t uart_addr = EXT_IO_BASE + 0x1000;

bus.add_device(rtc_addr, rtc.get());
bus.add_device(uart_addr, uart.get());

const int align = 0x1000;
reg_t cpu_addr = rtc_addr + ((rtc->size() - 1) / align + 1) * align;
reg_t cpu_addr = uart_addr + ((rtc->size() - 1) / align + 1) * align;
reg_t cpu_size = align;

uint32_t reset_vec[8] = {
@@ -176,6 +180,9 @@ void sim_t::make_config_string()
"rtc {\n"
" addr 0x" << rtc_addr << ";\n"
"};\n"
"uart {\n"
" addr 0x" << uart_addr << ";\n"
"};\n"
"ram {\n"
" 0 {\n"
" addr 0x" << DRAM_BASE << ";\n"
@@ -46,6 +46,7 @@ class sim_t
std::string config_string;
std::unique_ptr<rom_device_t> boot_rom;
std::unique_ptr<rtc_t> rtc;
std::unique_ptr<uart_t> uart;
bus_t bus;
debug_module_t debug_module;

@@ -0,0 +1,54 @@
/* This file implements a minimal 8250 UART block. */
#include "devices.h"
#include "processor.h"
#include <unistd.h>

enum {
UART_DATA = 0, /* Data RX/TX */
UART_INTR_EN, /* Interrupt enable */
UART_INTR_ID, /* Interrupt identification */
UART_LINE_CTL, /* Line control */
UART_MODEM_CTL, /* Modem control */
UART_LINE_STAT, /* Line status */
UART_MODEM_STAT, /* Modem status */
UART_SCRATCH /* Scratch register */
};

enum {
UART_LINE_STAT_THRE = (1 << 5), /* Transmitter holding register empty */
UART_LINE_STAT_TEMT = (1 << 6), /* Transmitter completely empty */
};

bool uart_t::load(reg_t addr, size_t len, uint8_t* bytes)
{
/* For simplicity on our side, only support byte-sized accesses for now. */
if (len > 1)
return false;

switch (addr)
{
case UART_LINE_STAT:
*bytes = UART_LINE_STAT_TEMT | UART_LINE_STAT_THRE;
break;
default:
*bytes = 0;
}

return true;
}

bool uart_t::store(reg_t addr, size_t len, const uint8_t* bytes)
{
if (len > 1)
return false;

switch (addr)
{
case UART_DATA:
putchar(*bytes);
fflush(stdout);
break;
}

return true;
}

0 comments on commit 6641189

Please sign in to comment.
You can’t perform that action at this time.