diff options
Diffstat (limited to 'riscv')
-rw-r--r-- | riscv/abstract_interrupt_controller.h | 14 | ||||
-rw-r--r-- | riscv/devices.h | 77 | ||||
-rw-r--r-- | riscv/dts.cc | 93 | ||||
-rw-r--r-- | riscv/dts.h | 5 | ||||
-rw-r--r-- | riscv/ns16550.cc | 315 | ||||
-rw-r--r-- | riscv/platform.h | 9 | ||||
-rw-r--r-- | riscv/plic.cc | 393 | ||||
-rw-r--r-- | riscv/processor.h | 1 | ||||
-rw-r--r-- | riscv/riscv.mk.in | 2 | ||||
-rw-r--r-- | riscv/sim.cc | 24 | ||||
-rw-r--r-- | riscv/sim.h | 2 |
11 files changed, 932 insertions, 3 deletions
diff --git a/riscv/abstract_interrupt_controller.h b/riscv/abstract_interrupt_controller.h new file mode 100644 index 0000000..946b079 --- /dev/null +++ b/riscv/abstract_interrupt_controller.h @@ -0,0 +1,14 @@ +#ifndef _RISCV_ABSTRACT_INTERRUPT_CONTROLLER_H +#define _RISCV_ABSTRACT_INTERRUPT_CONTROLLER_H + +#include "decode.h" +#include <cstdint> +#include <cstddef> + +class abstract_interrupt_controller_t { + public: + virtual void set_interrupt_level(uint32_t interrupt_id, int level) = 0; + virtual ~abstract_interrupt_controller_t() {} +}; + +#endif diff --git a/riscv/devices.h b/riscv/devices.h index 08e9c45..bccda34 100644 --- a/riscv/devices.h +++ b/riscv/devices.h @@ -4,8 +4,10 @@ #include "decode.h" #include "mmio_plugin.h" #include "abstract_device.h" +#include "abstract_interrupt_controller.h" #include "platform.h" #include <map> +#include <queue> #include <vector> #include <utility> @@ -74,6 +76,81 @@ class clint_t : public abstract_device_t { std::vector<mtimecmp_t> mtimecmp; }; +#define PLIC_MAX_DEVICES 1024 + +struct plic_context { + uint32_t num; + processor_t *proc; + bool mmode; + + uint8_t priority_threshold; + uint32_t enable[PLIC_MAX_DEVICES/32]; + uint32_t pending[PLIC_MAX_DEVICES/32]; + uint8_t pending_priority[PLIC_MAX_DEVICES]; + uint32_t claimed[PLIC_MAX_DEVICES/32]; +}; +typedef struct plic_context plic_context_t; + +class plic_t : public abstract_device_t, public abstract_interrupt_controller_t { + public: + plic_t(std::vector<processor_t*>&, bool smode, uint32_t ndev); + bool load(reg_t addr, size_t len, uint8_t* bytes); + bool store(reg_t addr, size_t len, const uint8_t* bytes); + void set_interrupt_level(uint32_t id, int lvl); + size_t size() { return PLIC_SIZE; } + private: + std::vector<processor_t*>& procs; + std::vector<plic_context_t> contexts; + uint32_t num_ids; + uint32_t num_ids_word; + uint32_t max_prio; + uint8_t priority[PLIC_MAX_DEVICES]; + uint32_t level[PLIC_MAX_DEVICES/32]; + uint32_t context_best_pending(plic_context_t *c); + void context_update(plic_context_t *context); + uint32_t context_claim(plic_context_t *c); + bool priority_read(reg_t offset, uint32_t *val); + bool priority_write(reg_t offset, uint32_t val); + bool context_enable_read(plic_context_t *context, + reg_t offset, uint32_t *val); + bool context_enable_write(plic_context_t *context, + reg_t offset, uint32_t val); + bool context_read(plic_context_t *context, + reg_t offset, uint32_t *val); + bool context_write(plic_context_t *context, + reg_t offset, uint32_t val); +}; + +class ns16550_t : public abstract_device_t { + public: + ns16550_t(class bus_t *bus, abstract_interrupt_controller_t *intctrl, + uint32_t interrupt_id, uint32_t reg_shift, uint32_t reg_io_width); + bool load(reg_t addr, size_t len, uint8_t* bytes); + bool store(reg_t addr, size_t len, const uint8_t* bytes); + void tick(void); + size_t size() { return NS16550_SIZE; } + private: + class bus_t *bus; + abstract_interrupt_controller_t *intctrl; + uint32_t interrupt_id; + uint32_t reg_shift; + uint32_t reg_io_width; + std::queue<uint8_t> rx_queue; + uint8_t dll; + uint8_t dlm; + uint8_t iir; + uint8_t ier; + uint8_t fcr; + uint8_t lcr; + uint8_t mcr; + uint8_t lsr; + uint8_t msr; + uint8_t scr; + void update_interrupt(void); + uint8_t rx_byte(void); + void tx_byte(uint8_t val); +}; + class mmio_plugin_device_t : public abstract_device_t { public: mmio_plugin_device_t(const std::string& name, const std::string& args); diff --git a/riscv/dts.cc b/riscv/dts.cc index 9937d57..6a40565 100644 --- a/riscv/dts.cc +++ b/riscv/dts.cc @@ -27,15 +27,16 @@ std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz, " #size-cells = <2>;\n" " compatible = \"ucbbar,spike-bare-dev\";\n" " model = \"ucbbar,spike-bare\";\n" - " chosen {\n"; + " chosen {\n" + " stdout-path = &SERIAL0;\n"; if (initrd_start < initrd_end) { s << " linux,initrd-start = <" << (size_t)initrd_start << ">;\n" " linux,initrd-end = <" << (size_t)initrd_end << ">;\n"; if (!bootargs) - bootargs = "root=/dev/ram console=hvc0 earlycon=sbi"; + bootargs = "root=/dev/ram console=ttyS0 earlycon"; } else { if (!bootargs) - bootargs = "console=hvc0 earlycon=sbi"; + bootargs = "console=ttyS0 earlycon"; } s << " bootargs = \""; for (size_t i = 0; i < strlen(bootargs); i++) { @@ -94,6 +95,34 @@ std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz, " reg = <0x" << (clintbs >> 32) << " 0x" << (clintbs & (uint32_t)-1) << " 0x" << (clintsz >> 32) << " 0x" << (clintsz & (uint32_t)-1) << ">;\n" " };\n" + " PLIC: plic@" << PLIC_BASE << " {\n" + " compatible = \"riscv,plic0\";\n" + " interrupts-extended = <" << std::dec; + for (size_t i = 0; i < procs.size(); i++) + s << "&CPU" << i << "_intc 11 &CPU" << i << "_intc 9 "; + reg_t plicbs = PLIC_BASE; + reg_t plicsz = PLIC_SIZE; + s << std::hex << ">;\n" + " reg = <0x" << (plicbs >> 32) << " 0x" << (plicbs & (uint32_t)-1) << + " 0x" << (plicsz >> 32) << " 0x" << (plicsz & (uint32_t)-1) << ">;\n" + " riscv,ndev = <0x" << PLIC_NDEV << ">;\n" + " riscv,max-priority = <0x" << ((1U << PLIC_PRIO_BITS) - 1) << ">;\n" + " #interrupt-cells = <1>;\n" + " interrupt-controller;\n" + " };\n" + " SERIAL0: ns16550@" << NS16550_BASE << " {\n" + " compatible = \"ns16550a\";\n" + " clock-frequency = <" << std::dec << (cpu_hz/insns_per_rtc_tick) << ">;\n" + " interrupt-parent = <&PLIC>;\n" + " interrupts = <" << std::dec << NS16550_INTERRUPT_ID; + reg_t ns16550bs = NS16550_BASE; + reg_t ns16550sz = NS16550_SIZE; + s << std::hex << ">;\n" + " reg = <0x" << (ns16550bs >> 32) << " 0x" << (ns16550bs & (uint32_t)-1) << + " 0x" << (ns16550sz >> 32) << " 0x" << (ns16550sz & (uint32_t)-1) << ">;\n" + " reg-shift = <0x" << NS16550_REG_SHIFT << ">;\n" + " reg-io-width = <0x" << NS16550_REG_IO_WIDTH << ">;\n" + " };\n" " };\n" " htif {\n" " compatible = \"ucb,htif0\";\n" @@ -276,6 +305,64 @@ int fdt_parse_clint(void *fdt, reg_t *clint_addr, return 0; } +int fdt_parse_plic(void *fdt, reg_t *plic_addr, uint32_t *ndev, + const char *compatible) +{ + int nodeoffset, len, rc; + const fdt32_t *ndev_p; + + nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compatible); + if (nodeoffset < 0) + return nodeoffset; + + rc = fdt_get_node_addr_size(fdt, nodeoffset, plic_addr, NULL, "reg"); + if (rc < 0 || !plic_addr) + return -ENODEV; + + ndev_p = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "riscv,ndev", &len); + if (!ndev || !ndev_p) + return -ENODEV; + *ndev = fdt32_to_cpu(*ndev_p); + + return 0; +} + +int fdt_parse_ns16550(void *fdt, reg_t *ns16550_addr, + uint32_t *reg_shift, uint32_t *reg_io_width, + const char *compatible) +{ + int nodeoffset, len, rc; + const fdt32_t *reg_p; + + nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compatible); + if (nodeoffset < 0) + return nodeoffset; + + rc = fdt_get_node_addr_size(fdt, nodeoffset, ns16550_addr, NULL, "reg"); + if (rc < 0 || !ns16550_addr) + return -ENODEV; + + reg_p = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "reg-shift", &len); + if (reg_shift) { + if (reg_p) { + *reg_shift = fdt32_to_cpu(*reg_p); + } else { + *reg_shift = NS16550_REG_SHIFT; + } + } + + reg_p = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "reg-io-width", &len); + if (reg_io_width) { + if (reg_p) { + *reg_io_width = fdt32_to_cpu(*reg_p); + } else { + *reg_io_width = NS16550_REG_IO_WIDTH; + } + } + + return 0; +} + int fdt_parse_pmp_num(void *fdt, int cpu_offset, reg_t *pmp_num) { int rc; diff --git a/riscv/dts.h b/riscv/dts.h index 1878ca1..7a64d7b 100644 --- a/riscv/dts.h +++ b/riscv/dts.h @@ -22,6 +22,11 @@ int fdt_get_next_subnode(void *fdt, int node); int fdt_parse_clint(void *fdt, reg_t *clint_addr, const char *compatible); +int fdt_parse_plic(void *fdt, reg_t *plic_addr, uint32_t *ndev, + const char *compatible); +int fdt_parse_ns16550(void *fdt, reg_t *ns16550_addr, + uint32_t *reg_shift, uint32_t *reg_io_width, + const char *compatible); int fdt_parse_pmp_num(void *fdt, int cpu_offset, reg_t *pmp_num); int fdt_parse_pmp_alignment(void *fdt, int cpu_offset, reg_t *pmp_align); int fdt_parse_mmu_type(void *fdt, int cpu_offset, const char **mmu_type); diff --git a/riscv/ns16550.cc b/riscv/ns16550.cc new file mode 100644 index 0000000..37af568 --- /dev/null +++ b/riscv/ns16550.cc @@ -0,0 +1,315 @@ +#include <sys/time.h> +#include "devices.h" +#include "processor.h" +#include "term.h" + +#define UART_QUEUE_SIZE 64 + +#define UART_RX 0 /* In: Receive buffer */ +#define UART_TX 0 /* Out: Transmit buffer */ + +#define UART_IER 1 /* Out: Interrupt Enable Register */ +#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */ +#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */ +#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */ +#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */ + +#define UART_IIR 2 /* In: Interrupt ID Register */ +#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ +#define UART_IIR_ID 0x0e /* Mask for the interrupt ID */ +#define UART_IIR_MSI 0x00 /* Modem status interrupt */ +#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ +#define UART_IIR_RDI 0x04 /* Receiver data interrupt */ +#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ + +#define UART_IIR_TYPE_BITS 0xc0 + +#define UART_FCR 2 /* Out: FIFO Control Register */ +#define UART_FCR_ENABLE_FIFO 0x01 /* Enable the FIFO */ +#define UART_FCR_CLEAR_RCVR 0x02 /* Clear the RCVR FIFO */ +#define UART_FCR_CLEAR_XMIT 0x04 /* Clear the XMIT FIFO */ +#define UART_FCR_DMA_SELECT 0x08 /* For DMA applications */ + +#define UART_LCR 3 /* Out: Line Control Register */ +#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ +#define UART_LCR_SBC 0x40 /* Set break control */ +#define UART_LCR_SPAR 0x20 /* Stick parity (?) */ +#define UART_LCR_EPAR 0x10 /* Even parity select */ +#define UART_LCR_PARITY 0x08 /* Parity Enable */ +#define UART_LCR_STOP 0x04 /* Stop bits: 0=1 bit, 1=2 bits */ + +#define UART_MCR 4 /* Out: Modem Control Register */ +#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */ +#define UART_MCR_OUT2 0x08 /* Out2 complement */ +#define UART_MCR_OUT1 0x04 /* Out1 complement */ +#define UART_MCR_RTS 0x02 /* RTS complement */ +#define UART_MCR_DTR 0x01 /* DTR complement */ + +#define UART_LSR 5 /* In: Line Status Register */ +#define UART_LSR_FIFOE 0x80 /* Fifo error */ +#define UART_LSR_TEMT 0x40 /* Transmitter empty */ +#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ +#define UART_LSR_BI 0x10 /* Break interrupt indicator */ +#define UART_LSR_FE 0x08 /* Frame error indicator */ +#define UART_LSR_PE 0x04 /* Parity error indicator */ +#define UART_LSR_OE 0x02 /* Overrun error indicator */ +#define UART_LSR_DR 0x01 /* Receiver data ready */ +#define UART_LSR_BRK_ERROR_BITS 0x1E /* BI, FE, PE, OE bits */ + +#define UART_MSR 6 /* In: Modem Status Register */ +#define UART_MSR_DCD 0x80 /* Data Carrier Detect */ +#define UART_MSR_RI 0x40 /* Ring Indicator */ +#define UART_MSR_DSR 0x20 /* Data Set Ready */ +#define UART_MSR_CTS 0x10 /* Clear to Send */ +#define UART_MSR_DDCD 0x08 /* Delta DCD */ +#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */ +#define UART_MSR_DDSR 0x02 /* Delta DSR */ +#define UART_MSR_DCTS 0x01 /* Delta CTS */ +#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */ + +#define UART_SCR 7 /* I/O: Scratch Register */ + +ns16550_t::ns16550_t(class bus_t *bus, abstract_interrupt_controller_t *intctrl, + uint32_t interrupt_id, uint32_t reg_shift, uint32_t reg_io_width) + : bus(bus), intctrl(intctrl), interrupt_id(interrupt_id), reg_shift(reg_shift), reg_io_width(reg_io_width) +{ + ier = 0; + iir = UART_IIR_NO_INT; + fcr = 0; + lcr = 0; + lsr = UART_LSR_TEMT | UART_LSR_THRE; + msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS; + dll = 0x0C; + mcr = UART_MCR_OUT2; + scr = 0; +} + +void ns16550_t::update_interrupt(void) +{ + uint8_t interrupts = 0; + + /* Handle clear rx */ + if (lcr & UART_FCR_CLEAR_RCVR) { + lcr &= ~UART_FCR_CLEAR_RCVR; + while (!rx_queue.empty()) { + rx_queue.pop(); + } + lsr &= ~UART_LSR_DR; + } + + /* Handle clear tx */ + if (lcr & UART_FCR_CLEAR_XMIT) { + lcr &= ~UART_FCR_CLEAR_XMIT; + lsr |= UART_LSR_TEMT | UART_LSR_THRE; + } + + /* Data ready and rcv interrupt enabled ? */ + if ((ier & UART_IER_RDI) && (lsr & UART_LSR_DR)) { + interrupts |= UART_IIR_RDI; + } + + /* Transmitter empty and interrupt enabled ? */ + if ((ier & UART_IER_THRI) && (lsr & UART_LSR_TEMT)) { + interrupts |= UART_IIR_THRI; + } + + /* Now update the interrup line, if necessary */ + if (!interrupts) { + iir = UART_IIR_NO_INT; + intctrl->set_interrupt_level(interrupt_id, 0); + } else { + iir = interrupts; + intctrl->set_interrupt_level(interrupt_id, 1); + } + + /* + * If the OS disabled the tx interrupt, we know that there is nothing + * more to transmit. + */ + if (!(ier & UART_IER_THRI)) { + lsr |= UART_LSR_TEMT | UART_LSR_THRE; + } +} + +uint8_t ns16550_t::rx_byte(void) +{ + uint8_t ret = 0; + + if (rx_queue.empty()) { + lsr &= ~UART_LSR_DR; + return 0; + } + + /* Break issued ? */ + if (lsr & UART_LSR_BI) { + lsr &= ~UART_LSR_BI; + return 0; + } + + ret = rx_queue.front(); + rx_queue.pop(); + if (rx_queue.empty()) { + lsr &= ~UART_LSR_DR; + } + + return ret; +} + +void ns16550_t::tx_byte(uint8_t val) +{ + lsr |= UART_LSR_TEMT | UART_LSR_THRE; + canonical_terminal_t::write(val); +} + +bool ns16550_t::load(reg_t addr, size_t len, uint8_t* bytes) +{ + uint8_t val; + bool ret = true, update = false; + + if (reg_io_width != len) { + return false; + } + addr >>= reg_shift; + addr &= 7; + + switch (addr) { + case UART_RX: + if (lcr & UART_LCR_DLAB) { + val = dll; + } else { + val = rx_byte(); + } + update = true; + break; + case UART_IER: + if (lcr & UART_LCR_DLAB) { + val = dlm; + } else { + val = ier; + } + break; + case UART_IIR: + val = iir | UART_IIR_TYPE_BITS; + break; + case UART_LCR: + val = lcr; + break; + case UART_MCR: + val = mcr; + break; + case UART_LSR: + val = lsr; + break; + case UART_MSR: + val = msr; + break; + case UART_SCR: + val = scr; + break; + default: + ret = false; + break; + }; + + if (ret) { + bytes[0] = val; + } + if (update) { + update_interrupt(); + } + + return ret; +} + +bool ns16550_t::store(reg_t addr, size_t len, const uint8_t* bytes) +{ + uint8_t val; + bool ret = true, update = false; + + if (reg_io_width != len) { + return false; + } + addr >>= reg_shift; + addr &= 7; + val = bytes[0]; + + switch (addr) { + case UART_TX: + update = true; + + if (lcr & UART_LCR_DLAB) { + dll = val; + break; + } + + /* Loopback mode */ + if (mcr & UART_MCR_LOOP) { + if (rx_queue.size() < UART_QUEUE_SIZE) { + rx_queue.push(val); + lsr |= UART_LSR_DR; + } + break; + } + + tx_byte(val); + break; + case UART_IER: + if (!(lcr & UART_LCR_DLAB)) { + ier = val & 0x0f; + } else { + dlm = val; + } + update = true; + break; + case UART_FCR: + fcr = val; + update = true; + break; + case UART_LCR: + lcr = val; + update = true; + break; + case UART_MCR: + mcr = val; + update = true; + break; + case UART_LSR: + /* Factory test */ + break; + case UART_MSR: + /* Not used */ + break; + case UART_SCR: + scr = val; + break; + default: + ret = false; + break; + }; + + if (update) { + update_interrupt(); + } + + return ret; +} + +void ns16550_t::tick(void) +{ + int rc; + + if (!(fcr & UART_FCR_ENABLE_FIFO) || + (mcr & UART_MCR_LOOP) || + (UART_QUEUE_SIZE <= rx_queue.size())) { + return; + } + + rc = canonical_terminal_t::read(); + if (rc < 0) { + return; + } + + rx_queue.push((uint8_t)rc); + lsr |= UART_LSR_DR; + update_interrupt(); +} diff --git a/riscv/platform.h b/riscv/platform.h index 6618d44..2bafa68 100644 --- a/riscv/platform.h +++ b/riscv/platform.h @@ -5,6 +5,15 @@ #define DEFAULT_RSTVEC 0x00001000 #define CLINT_BASE 0x02000000 #define CLINT_SIZE 0x000c0000 +#define PLIC_BASE 0x0c000000 +#define PLIC_SIZE 0x01000000 +#define PLIC_NDEV 31 +#define PLIC_PRIO_BITS 4 +#define NS16550_BASE 0x10000000 +#define NS16550_SIZE 0x100 +#define NS16550_REG_SHIFT 0 +#define NS16550_REG_IO_WIDTH 1 +#define NS16550_INTERRUPT_ID 1 #define EXT_IO_BASE 0x40000000 #define DRAM_BASE 0x80000000 diff --git a/riscv/plic.cc b/riscv/plic.cc new file mode 100644 index 0000000..2d08c8e --- /dev/null +++ b/riscv/plic.cc @@ -0,0 +1,393 @@ +#include <sys/time.h> +#include "devices.h" +#include "processor.h" + +#define PLIC_MAX_CONTEXTS 15872 + +/* + * The PLIC consists of memory-mapped control registers, with a memory map + * as follows: + * + * base + 0x000000: Reserved (interrupt source 0 does not exist) + * base + 0x000004: Interrupt source 1 priority + * base + 0x000008: Interrupt source 2 priority + * ... + * base + 0x000FFC: Interrupt source 1023 priority + * base + 0x001000: Pending 0 + * base + 0x001FFF: Pending + * base + 0x002000: Enable bits for sources 0-31 on context 0 + * base + 0x002004: Enable bits for sources 32-63 on context 0 + * ... + * base + 0x0020FC: Enable bits for sources 992-1023 on context 0 + * base + 0x002080: Enable bits for sources 0-31 on context 1 + * ... + * base + 0x002100: Enable bits for sources 0-31 on context 2 + * ... + * base + 0x1F1F80: Enable bits for sources 992-1023 on context 15871 + * base + 0x1F1F84: Reserved + * ... (higher context IDs would fit here, but wouldn't fit + * inside the per-context priority vector) + * base + 0x1FFFFC: Reserved + * base + 0x200000: Priority threshold for context 0 + * base + 0x200004: Claim/complete for context 0 + * base + 0x200008: Reserved + * ... + * base + 0x200FFC: Reserved + * base + 0x201000: Priority threshold for context 1 + * base + 0x201004: Claim/complete for context 1 + * ... + * base + 0xFFE000: Priority threshold for context 15871 + * base + 0xFFE004: Claim/complete for context 15871 + * base + 0xFFE008: Reserved + * ... + * base + 0xFFFFFC: Reserved + */ + +/* Each interrupt source has a priority register associated with it. */ +#define PRIORITY_BASE 0 +#define PRIORITY_PER_ID 4 + +/* + * Each hart context has a vector of interupt enable bits associated with it. + * There's one bit for each interrupt source. + */ +#define ENABLE_BASE 0x2000 +#define ENABLE_PER_HART 0x80 + +/* + * Each hart context has a set of control registers associated with it. Right + * now there's only two: a source priority threshold over which the hart will + * take an interrupt, and a register to claim interrupts. + */ +#define CONTEXT_BASE 0x200000 +#define CONTEXT_PER_HART 0x1000 +#define CONTEXT_THRESHOLD 0 +#define CONTEXT_CLAIM 4 + +#define REG_SIZE 0x1000000 + +plic_t::plic_t(std::vector<processor_t*>& procs, bool smode, uint32_t ndev) + : procs(procs), contexts(procs.size() * (smode ? 2 : 1)) +{ + size_t i; + size_t contexts_per_hart = smode ? 2 : 1; + plic_context_t *c; + + num_ids = ndev + 1; + num_ids_word = num_ids / 32; + if ((num_ids_word * 32) < num_ids) + num_ids_word++; + max_prio = (1UL << PLIC_PRIO_BITS) - 1; + memset(priority, 0, sizeof(priority)); + memset(level, 0, sizeof(level)); + + for (i = 0; i < contexts.size(); i++) { + c = &contexts[i]; + c->num = i; + c->proc = procs[i / contexts_per_hart]; + if (smode) { + c->mmode = (i % contexts_per_hart == 0); + } else { + c->mmode = true; + } + memset(&c->enable, 0, sizeof(c->enable)); + memset(&c->pending, 0, sizeof(c->pending)); + memset(&c->pending_priority, 0, sizeof(c->pending_priority)); + memset(&c->claimed, 0, sizeof(c->claimed)); + } +} + +uint32_t plic_t::context_best_pending(plic_context_t *c) +{ + uint8_t best_id_prio = 0; + uint32_t i, j, id, best_id = 0; + + for (i = 0; i < num_ids_word; i++) { + if (!c->pending[i]) { + continue; + } + + for (j = 0; j < 32; j++) { + id = i * 32 + j; + if ((num_ids <= id) || + !(c->pending[i] & (1 << j)) || + (c->claimed[i] & (1 << j))) { + continue; + } + + if (!best_id || + (best_id_prio < c->pending_priority[id])) { + best_id = id; + best_id_prio = c->pending_priority[id]; + } + } + } + + return best_id; +} + +void plic_t::context_update(plic_context_t *c) +{ + uint32_t best_id = context_best_pending(c); + reg_t mask = c->mmode ? MIP_MEIP : MIP_SEIP; + + c->proc->state.mip->backdoor_write_with_mask(mask, best_id ? mask : 0); +} + +uint32_t plic_t::context_claim(plic_context_t *c) +{ + uint32_t best_id = context_best_pending(c); + uint32_t best_id_word = best_id / 32; + uint32_t best_id_mask = (1 << (best_id % 32)); + + if (best_id) { + c->claimed[best_id_word] |= best_id_mask; + } + + context_update(c); + return best_id; +} + +bool plic_t::priority_read(reg_t offset, uint32_t *val) +{ + uint32_t id = (offset >> 2); + + if (id == 0 || id >= num_ids) { + return false; + } + + *val = priority[id]; + return true; +} + +bool plic_t::priority_write(reg_t offset, uint32_t val) +{ + uint32_t id = (offset >> 2); + + if (id == 0 || id >= num_ids) { + return false; + } + + val &= ((1 << PLIC_PRIO_BITS) - 1); + priority[id] = val; + return true; +} + +bool plic_t::context_enable_read(plic_context_t *c, + reg_t offset, uint32_t *val) +{ + uint32_t id_word = offset >> 2; + + if (num_ids_word < id_word) { + return false; + } + + *val = c->enable[id_word]; + return true; +} + +bool plic_t::context_enable_write(plic_context_t *c, + reg_t offset, uint32_t val) +{ + uint8_t id_prio; + uint32_t i, id, id_mask; + uint32_t id_word = offset >> 2; + uint32_t old_val, new_val, xor_val; + + if (num_ids_word < id_word) { + return false; + } + + old_val = c->enable[id_word]; + new_val = val; + + if (id_word == 0) { + new_val &= ~0x1; + } + + c->enable[id_word] = new_val; + + xor_val = old_val ^ new_val; + for (i = 0; i < 32; i++) { + id = id_word * 32 + i; + id_mask = 1 << i; + id_prio = priority[id]; + if (!(xor_val & id_mask)) { + continue; + } + if ((new_val & id_mask) && + (level[id_word] & id_mask)) { + c->pending[id_word] |= id_mask; + c->pending_priority[id] = id_prio; + } else if (!(new_val & id_mask)) { + c->pending[id_word] &= ~id_mask; + c->pending_priority[id] = 0; + c->claimed[id_word] &= ~id_mask; + } + } + + context_update(c); + return true; +} + +bool plic_t::context_read(plic_context_t *c, + reg_t offset, uint32_t *val) +{ + bool ret = true; + + switch (offset) { + case CONTEXT_THRESHOLD: + *val = c->priority_threshold; + break; + case CONTEXT_CLAIM: + *val = context_claim(c); + break; + default: + ret = false; + break; + }; + + return ret; +} + +bool plic_t::context_write(plic_context_t *c, + reg_t offset, uint32_t val) +{ + uint32_t id_word, id_mask; + bool ret = true, update = false; + + switch (offset) { + case CONTEXT_THRESHOLD: + val &= ((1 << PLIC_PRIO_BITS) - 1); + if (val <= max_prio) + c->priority_threshold = val; + else + update = true; + break; + case CONTEXT_CLAIM: + id_word = val / 32; + id_mask = 1 << (val % 32); + if ((val < num_ids) && + (c->enable[id_word] & id_mask)) { + c->claimed[id_word] &= ~id_mask; + update = true; + } + break; + default: + ret = false; + update = true; + break; + }; + + if (update) { + context_update(c); + } + + return ret; +} + +void plic_t::set_interrupt_level(uint32_t id, int lvl) +{ + uint8_t i, id_prio, id_word; + uint32_t id_mask; + plic_context_t *c = NULL; + + if (id <= 0 || num_ids <= id) { + return; + } + + id_prio = priority[id]; + id_word = id / 32; + id_mask = 1 << (id % 32); + + if (lvl) { + level[id_word] |= id_mask; + } else { + level[id_word] &= ~id_mask; + } + + /* + * Note: PLIC interrupts are level-triggered. As of now, + * there is no notion of edge-triggered interrupts. To + * handle this we auto-clear edge-triggered interrupts + * when PLIC context CLAIM register is read. + */ + for (i = 0; i < contexts.size(); i++) { + c = &contexts[i]; + + if (c->enable[id_word] & id_mask) { + if (lvl) { + c->pending[id_word] |= id_mask; + c->pending_priority[id] = id_prio; + } else { + c->pending[id_word] &= ~id_mask; + c->pending_priority[id] = 0; + c->claimed[id_word] &= ~id_mask; + } + context_update(c); + break; + } + } +} + +bool plic_t::load(reg_t addr, size_t len, uint8_t* bytes) +{ + bool ret = false; + uint32_t cntx, val = 0; + + /* Only 32bit loads supported */ + if (len != 4) { + return false; + } + + if (PRIORITY_BASE <= addr && addr < ENABLE_BASE) { + ret = priority_read(addr, &val); + } else if (ENABLE_BASE <= addr && addr < CONTEXT_BASE) { + cntx = (addr - ENABLE_BASE) / ENABLE_PER_HART; + addr -= cntx * ENABLE_PER_HART + ENABLE_BASE; + if (cntx < contexts.size()) { + ret = context_enable_read(&contexts[cntx], addr, &val); + } + } else if (CONTEXT_BASE <= addr && addr < REG_SIZE) { + cntx = (addr - CONTEXT_BASE) / CONTEXT_PER_HART; + addr -= cntx * CONTEXT_PER_HART + CONTEXT_BASE; + if (cntx < contexts.size()) { + ret = context_read(&contexts[cntx], addr, &val); + } + } + + if (ret) { + memcpy(bytes, (uint8_t *)&val, len); + } + + return ret; +} + +bool plic_t::store(reg_t addr, size_t len, const uint8_t* bytes) +{ + bool ret = false; + uint32_t cntx, val; + + /* Only 32bit stores supported */ + if (len != 4) { + return false; + } + + memcpy((uint8_t *)&val, bytes, len); + + if (PRIORITY_BASE <= addr && addr < ENABLE_BASE) { + ret = priority_write(addr, val); + } else if (ENABLE_BASE <= addr && addr < CONTEXT_BASE) { + cntx = (addr - ENABLE_BASE) / ENABLE_PER_HART; + addr -= cntx * ENABLE_PER_HART + ENABLE_BASE; + if (cntx < contexts.size()) + ret = context_enable_write(&contexts[cntx], addr, val); + } else if (CONTEXT_BASE <= addr && addr < REG_SIZE) { + cntx = (addr - CONTEXT_BASE) / CONTEXT_PER_HART; + addr -= cntx * CONTEXT_PER_HART + CONTEXT_BASE; + if (cntx < contexts.size()) + ret = context_write(&contexts[cntx], addr, val); + } + + return ret; +} diff --git a/riscv/processor.h b/riscv/processor.h index fc80914..8194046 100644 --- a/riscv/processor.h +++ b/riscv/processor.h @@ -365,6 +365,7 @@ private: friend class mmu_t; friend class clint_t; + friend class plic_t; friend class extension_t; void parse_varch_string(const char*); diff --git a/riscv/riscv.mk.in b/riscv/riscv.mk.in index bbac794..91f7a5f 100644 --- a/riscv/riscv.mk.in +++ b/riscv/riscv.mk.in @@ -87,6 +87,8 @@ riscv_srcs = \ devices.cc \ rom.cc \ clint.cc \ + plic.cc \ + ns16550.cc \ debug_module.cc \ remote_bitbang.cc \ jtag_dtm.cc \ diff --git a/riscv/sim.cc b/riscv/sim.cc index 8a15560..5ce7d21 100644 --- a/riscv/sim.cc +++ b/riscv/sim.cc @@ -98,6 +98,29 @@ sim_t::sim_t(const cfg_t *cfg, bool halted, bus.add_device(clint_base, clint.get()); } + // pointer to wired interrupt controller + abstract_interrupt_controller_t *intctrl = NULL; + + // create plic + reg_t plic_base; + uint32_t plic_ndev; + if (fdt_parse_plic(fdt, &plic_base, &plic_ndev, "riscv,plic0") == 0) { + plic.reset(new plic_t(procs, true, plic_ndev)); + bus.add_device(plic_base, plic.get()); + intctrl = plic.get(); + } + + // create ns16550 + reg_t ns16550_base; + uint32_t ns16550_shift, ns16550_io_width; + if (fdt_parse_ns16550(fdt, &ns16550_base, + &ns16550_shift, &ns16550_io_width, "ns16550a") == 0) { + assert(intctrl); + ns16550.reset(new ns16550_t(&bus, intctrl, NS16550_INTERRUPT_ID, + ns16550_shift, ns16550_io_width)); + bus.add_device(ns16550_base, ns16550.get()); + } + //per core attribute int cpu_offset = 0, rc; size_t cpu_idx = 0; @@ -221,6 +244,7 @@ void sim_t::step(size_t n) if (++current_proc == procs.size()) { current_proc = 0; if (clint) clint->increment(INTERLEAVE / INSNS_PER_RTC_TICK); + if (ns16550) ns16550->tick(); } host->switch_to(); diff --git a/riscv/sim.h b/riscv/sim.h index c7dbd47..7816b87 100644 --- a/riscv/sim.h +++ b/riscv/sim.h @@ -82,6 +82,8 @@ private: bool dtb_enabled; std::unique_ptr<rom_device_t> boot_rom; std::unique_ptr<clint_t> clint; + std::unique_ptr<plic_t> plic; + std::unique_ptr<ns16550_t> ns16550; bus_t bus; log_file_t log_file; |