This is a range of patches for RISC-V.
Some key points are: - Generalise the CPU init functions - Support the SiFive revB machine - Improvements to the Hypervisor implementation and error checking - Connect some OpenTitan devices - Changes to the sifive_u machine to support U-boot v2: - Fix missing realise assert -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEE9sSsRtSTSGjTuM6PIeENKd+XcFQFAl7s6RoACgkQIeENKd+X cFSLjQgAhYMMjjuj/W/+UQ6p06aZcM36L/4CzNrJwgQKwvMIVhNcbnWNI80XNEmA 3acAeSexwfN+QgXB2WxIlBNx0qH8pc/LwGgDqcQ5KLehpqWXIgC3MJ0VgfwpNz8n YXkmm/QaPfgeZeNi6GhF6mSpn1o7dpjeGH78UOuXCfnWREW1NtgVnKbd3WrhS9gU QBwy2lVIp64+K6hb579UwT6M4KqM0eI6WJF9arhinV7aT2YtD5zM/F4BoYMSR571 dz4yDD/6YeHxCu4rI8Vo1R06yGpNt0RPsuqbtUFmmqyTuC7cQ+IKMoD/N+SBJHpm SsTid492rxpNlD0WDJrynOnSHW4LFA== =QHFB -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/alistair/tags/pull-riscv-to-apply-20200619-3' into staging This is a range of patches for RISC-V. Some key points are: - Generalise the CPU init functions - Support the SiFive revB machine - Improvements to the Hypervisor implementation and error checking - Connect some OpenTitan devices - Changes to the sifive_u machine to support U-boot v2: - Fix missing realise assert # gpg: Signature made Fri 19 Jun 2020 17:34:34 BST # gpg: using RSA key F6C4AC46D4934868D3B8CE8F21E10D29DF977054 # gpg: Good signature from "Alistair Francis <alistair@alistair23.me>" [full] # Primary key fingerprint: F6C4 AC46 D493 4868 D3B8 CE8F 21E1 0D29 DF97 7054 * remotes/alistair/tags/pull-riscv-to-apply-20200619-3: (32 commits) hw/riscv: sifive_u: Add a dummy DDR memory controller device hw/riscv: sifive_u: Sort the SoC memmap table entries hw/riscv: sifive_u: Support different boot source per MSEL pin state hw/riscv: sifive: Change SiFive E/U CPU reset vector to 0x1004 target/riscv: Rename IBEX CPU init routine hw/riscv: sifive_u: Add a new property msel for MSEL pin state hw/riscv: sifive_u: Rename serial property get/set functions to a generic name hw/riscv: sifive_u: Add reset functionality hw/riscv: sifive_gpio: Do not blindly trigger output IRQs hw/riscv: sifive_u: Hook a GPIO controller hw/riscv: sifive_gpio: Add a new 'ngpio' property hw/riscv: sifive_gpio: Clean up the codes hw/riscv: sifive_u: Generate device tree node for OTP hw/riscv: sifive_u: Simplify the GEM IRQ connect code a little bit hw/riscv: opentitan: Remove the riscv_ prefix of the machine* and soc* functions hw/riscv: sifive_e: Remove the riscv_ prefix of the machine* and soc* functions target/riscv: Use a smaller guess size for no-MMU PMP riscv/opentitan: Connect the UART device riscv/opentitan: Connect the PLIC device hw/intc: Initial commit of lowRISC Ibex PLIC ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
171199f56f
|
@ -1250,7 +1250,11 @@ M: Alistair Francis <Alistair.Francis@wdc.com>
|
|||
L: qemu-riscv@nongnu.org
|
||||
S: Supported
|
||||
F: hw/riscv/opentitan.c
|
||||
F: hw/char/ibex_uart.c
|
||||
F: hw/intc/ibex_plic.c
|
||||
F: include/hw/riscv/opentitan.h
|
||||
F: include/hw/char/ibex_uart.h
|
||||
F: include/hw/intc/ibex_plic.h
|
||||
|
||||
SH4 Machines
|
||||
------------
|
||||
|
|
|
@ -12,6 +12,7 @@ common-obj-$(CONFIG_VIRTIO_SERIAL) += virtio-console.o
|
|||
common-obj-$(CONFIG_XILINX) += xilinx_uartlite.o
|
||||
common-obj-$(CONFIG_XEN) += xen_console.o
|
||||
common-obj-$(CONFIG_CADENCE) += cadence_uart.o
|
||||
common-obj-$(CONFIG_IBEX) += ibex_uart.o
|
||||
|
||||
common-obj-$(CONFIG_EXYNOS4) += exynos4210_uart.o
|
||||
common-obj-$(CONFIG_COLDFIRE) += mcf_uart.o
|
||||
|
|
492
hw/char/ibex_uart.c
Normal file
492
hw/char/ibex_uart.c
Normal file
|
@ -0,0 +1,492 @@
|
|||
/*
|
||||
* QEMU lowRISC Ibex UART device
|
||||
*
|
||||
* Copyright (c) 2020 Western Digital
|
||||
*
|
||||
* For details check the documentation here:
|
||||
* https://docs.opentitan.org/hw/ip/uart/doc/
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/char/ibex_uart.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
static void ibex_uart_update_irqs(IbexUartState *s)
|
||||
{
|
||||
if (s->uart_intr_state & s->uart_intr_enable & INTR_STATE_TX_WATERMARK) {
|
||||
qemu_set_irq(s->tx_watermark, 1);
|
||||
} else {
|
||||
qemu_set_irq(s->tx_watermark, 0);
|
||||
}
|
||||
|
||||
if (s->uart_intr_state & s->uart_intr_enable & INTR_STATE_RX_WATERMARK) {
|
||||
qemu_set_irq(s->rx_watermark, 1);
|
||||
} else {
|
||||
qemu_set_irq(s->rx_watermark, 0);
|
||||
}
|
||||
|
||||
if (s->uart_intr_state & s->uart_intr_enable & INTR_STATE_TX_EMPTY) {
|
||||
qemu_set_irq(s->tx_empty, 1);
|
||||
} else {
|
||||
qemu_set_irq(s->tx_empty, 0);
|
||||
}
|
||||
|
||||
if (s->uart_intr_state & s->uart_intr_enable & INTR_STATE_RX_OVERFLOW) {
|
||||
qemu_set_irq(s->rx_overflow, 1);
|
||||
} else {
|
||||
qemu_set_irq(s->rx_overflow, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int ibex_uart_can_receive(void *opaque)
|
||||
{
|
||||
IbexUartState *s = opaque;
|
||||
|
||||
if (s->uart_ctrl & UART_CTRL_RX_ENABLE) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ibex_uart_receive(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
IbexUartState *s = opaque;
|
||||
uint8_t rx_fifo_level = (s->uart_fifo_ctrl & FIFO_CTRL_RXILVL)
|
||||
>> FIFO_CTRL_RXILVL_SHIFT;
|
||||
|
||||
s->uart_rdata = *buf;
|
||||
|
||||
s->uart_status &= ~UART_STATUS_RXIDLE;
|
||||
s->uart_status &= ~UART_STATUS_RXEMPTY;
|
||||
|
||||
if (size > rx_fifo_level) {
|
||||
s->uart_intr_state |= INTR_STATE_RX_WATERMARK;
|
||||
}
|
||||
|
||||
ibex_uart_update_irqs(s);
|
||||
}
|
||||
|
||||
static gboolean ibex_uart_xmit(GIOChannel *chan, GIOCondition cond,
|
||||
void *opaque)
|
||||
{
|
||||
IbexUartState *s = opaque;
|
||||
uint8_t tx_fifo_level = (s->uart_fifo_ctrl & FIFO_CTRL_TXILVL)
|
||||
>> FIFO_CTRL_TXILVL_SHIFT;
|
||||
int ret;
|
||||
|
||||
/* instant drain the fifo when there's no back-end */
|
||||
if (!qemu_chr_fe_backend_connected(&s->chr)) {
|
||||
s->tx_level = 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!s->tx_level) {
|
||||
s->uart_status &= ~UART_STATUS_TXFULL;
|
||||
s->uart_status |= UART_STATUS_TXEMPTY;
|
||||
s->uart_intr_state |= INTR_STATE_TX_EMPTY;
|
||||
s->uart_intr_state &= ~INTR_STATE_TX_WATERMARK;
|
||||
ibex_uart_update_irqs(s);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ret = qemu_chr_fe_write(&s->chr, s->tx_fifo, s->tx_level);
|
||||
|
||||
if (ret >= 0) {
|
||||
s->tx_level -= ret;
|
||||
memmove(s->tx_fifo, s->tx_fifo + ret, s->tx_level);
|
||||
}
|
||||
|
||||
if (s->tx_level) {
|
||||
guint r = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP,
|
||||
ibex_uart_xmit, s);
|
||||
if (!r) {
|
||||
s->tx_level = 0;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear the TX Full bit */
|
||||
if (s->tx_level != IBEX_UART_TX_FIFO_SIZE) {
|
||||
s->uart_status &= ~UART_STATUS_TXFULL;
|
||||
}
|
||||
|
||||
/* Disable the TX_WATERMARK IRQ */
|
||||
if (s->tx_level < tx_fifo_level) {
|
||||
s->uart_intr_state &= ~INTR_STATE_TX_WATERMARK;
|
||||
}
|
||||
|
||||
/* Set TX empty */
|
||||
if (s->tx_level == 0) {
|
||||
s->uart_status |= UART_STATUS_TXEMPTY;
|
||||
s->uart_intr_state |= INTR_STATE_TX_EMPTY;
|
||||
}
|
||||
|
||||
ibex_uart_update_irqs(s);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void uart_write_tx_fifo(IbexUartState *s, const uint8_t *buf,
|
||||
int size)
|
||||
{
|
||||
uint64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
uint8_t tx_fifo_level = (s->uart_fifo_ctrl & FIFO_CTRL_TXILVL)
|
||||
>> FIFO_CTRL_TXILVL_SHIFT;
|
||||
|
||||
if (size > IBEX_UART_TX_FIFO_SIZE - s->tx_level) {
|
||||
size = IBEX_UART_TX_FIFO_SIZE - s->tx_level;
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "ibex_uart: TX FIFO overflow");
|
||||
}
|
||||
|
||||
memcpy(s->tx_fifo + s->tx_level, buf, size);
|
||||
s->tx_level += size;
|
||||
|
||||
if (s->tx_level > 0) {
|
||||
s->uart_status &= ~UART_STATUS_TXEMPTY;
|
||||
}
|
||||
|
||||
if (s->tx_level >= tx_fifo_level) {
|
||||
s->uart_intr_state |= INTR_STATE_TX_WATERMARK;
|
||||
ibex_uart_update_irqs(s);
|
||||
}
|
||||
|
||||
if (s->tx_level == IBEX_UART_TX_FIFO_SIZE) {
|
||||
s->uart_status |= UART_STATUS_TXFULL;
|
||||
}
|
||||
|
||||
timer_mod(s->fifo_trigger_handle, current_time +
|
||||
(s->char_tx_time * 4));
|
||||
}
|
||||
|
||||
static void ibex_uart_reset(DeviceState *dev)
|
||||
{
|
||||
IbexUartState *s = IBEX_UART(dev);
|
||||
|
||||
s->uart_intr_state = 0x00000000;
|
||||
s->uart_intr_state = 0x00000000;
|
||||
s->uart_intr_enable = 0x00000000;
|
||||
s->uart_ctrl = 0x00000000;
|
||||
s->uart_status = 0x0000003c;
|
||||
s->uart_rdata = 0x00000000;
|
||||
s->uart_fifo_ctrl = 0x00000000;
|
||||
s->uart_fifo_status = 0x00000000;
|
||||
s->uart_ovrd = 0x00000000;
|
||||
s->uart_val = 0x00000000;
|
||||
s->uart_timeout_ctrl = 0x00000000;
|
||||
|
||||
s->tx_level = 0;
|
||||
|
||||
s->char_tx_time = (NANOSECONDS_PER_SECOND / 230400) * 10;
|
||||
|
||||
ibex_uart_update_irqs(s);
|
||||
}
|
||||
|
||||
static uint64_t ibex_uart_read(void *opaque, hwaddr addr,
|
||||
unsigned int size)
|
||||
{
|
||||
IbexUartState *s = opaque;
|
||||
uint64_t retvalue = 0;
|
||||
|
||||
switch (addr) {
|
||||
case IBEX_UART_INTR_STATE:
|
||||
retvalue = s->uart_intr_state;
|
||||
break;
|
||||
case IBEX_UART_INTR_ENABLE:
|
||||
retvalue = s->uart_intr_enable;
|
||||
break;
|
||||
case IBEX_UART_INTR_TEST:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: wdata is write only\n", __func__);
|
||||
break;
|
||||
|
||||
case IBEX_UART_CTRL:
|
||||
retvalue = s->uart_ctrl;
|
||||
break;
|
||||
case IBEX_UART_STATUS:
|
||||
retvalue = s->uart_status;
|
||||
break;
|
||||
|
||||
case IBEX_UART_RDATA:
|
||||
retvalue = s->uart_rdata;
|
||||
if (s->uart_ctrl & UART_CTRL_RX_ENABLE) {
|
||||
qemu_chr_fe_accept_input(&s->chr);
|
||||
|
||||
s->uart_status |= UART_STATUS_RXIDLE;
|
||||
s->uart_status |= UART_STATUS_RXEMPTY;
|
||||
}
|
||||
break;
|
||||
case IBEX_UART_WDATA:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: wdata is write only\n", __func__);
|
||||
break;
|
||||
|
||||
case IBEX_UART_FIFO_CTRL:
|
||||
retvalue = s->uart_fifo_ctrl;
|
||||
break;
|
||||
case IBEX_UART_FIFO_STATUS:
|
||||
retvalue = s->uart_fifo_status;
|
||||
|
||||
retvalue |= s->tx_level & 0x1F;
|
||||
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: RX fifos are not supported\n", __func__);
|
||||
break;
|
||||
|
||||
case IBEX_UART_OVRD:
|
||||
retvalue = s->uart_ovrd;
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: ovrd is not supported\n", __func__);
|
||||
break;
|
||||
case IBEX_UART_VAL:
|
||||
retvalue = s->uart_val;
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: val is not supported\n", __func__);
|
||||
break;
|
||||
case IBEX_UART_TIMEOUT_CTRL:
|
||||
retvalue = s->uart_timeout_ctrl;
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: timeout_ctrl is not supported\n", __func__);
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return retvalue;
|
||||
}
|
||||
|
||||
static void ibex_uart_write(void *opaque, hwaddr addr,
|
||||
uint64_t val64, unsigned int size)
|
||||
{
|
||||
IbexUartState *s = opaque;
|
||||
uint32_t value = val64;
|
||||
|
||||
switch (addr) {
|
||||
case IBEX_UART_INTR_STATE:
|
||||
/* Write 1 clear */
|
||||
s->uart_intr_state &= ~value;
|
||||
ibex_uart_update_irqs(s);
|
||||
break;
|
||||
case IBEX_UART_INTR_ENABLE:
|
||||
s->uart_intr_enable = value;
|
||||
ibex_uart_update_irqs(s);
|
||||
break;
|
||||
case IBEX_UART_INTR_TEST:
|
||||
s->uart_intr_state |= value;
|
||||
ibex_uart_update_irqs(s);
|
||||
break;
|
||||
|
||||
case IBEX_UART_CTRL:
|
||||
s->uart_ctrl = value;
|
||||
|
||||
if (value & UART_CTRL_NF) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: UART_CTRL_NF is not supported\n", __func__);
|
||||
}
|
||||
if (value & UART_CTRL_SLPBK) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: UART_CTRL_SLPBK is not supported\n", __func__);
|
||||
}
|
||||
if (value & UART_CTRL_LLPBK) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: UART_CTRL_LLPBK is not supported\n", __func__);
|
||||
}
|
||||
if (value & UART_CTRL_PARITY_EN) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: UART_CTRL_PARITY_EN is not supported\n",
|
||||
__func__);
|
||||
}
|
||||
if (value & UART_CTRL_PARITY_ODD) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: UART_CTRL_PARITY_ODD is not supported\n",
|
||||
__func__);
|
||||
}
|
||||
if (value & UART_CTRL_RXBLVL) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: UART_CTRL_RXBLVL is not supported\n", __func__);
|
||||
}
|
||||
if (value & UART_CTRL_NCO) {
|
||||
uint64_t baud = ((value & UART_CTRL_NCO) >> 16);
|
||||
baud *= 1000;
|
||||
baud /= 2 ^ 20;
|
||||
|
||||
s->char_tx_time = (NANOSECONDS_PER_SECOND / baud) * 10;
|
||||
}
|
||||
break;
|
||||
case IBEX_UART_STATUS:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: status is read only\n", __func__);
|
||||
break;
|
||||
|
||||
case IBEX_UART_RDATA:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: rdata is read only\n", __func__);
|
||||
break;
|
||||
case IBEX_UART_WDATA:
|
||||
uart_write_tx_fifo(s, (uint8_t *) &value, 1);
|
||||
break;
|
||||
|
||||
case IBEX_UART_FIFO_CTRL:
|
||||
s->uart_fifo_ctrl = value;
|
||||
|
||||
if (value & FIFO_CTRL_RXRST) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: RX fifos are not supported\n", __func__);
|
||||
}
|
||||
if (value & FIFO_CTRL_TXRST) {
|
||||
s->tx_level = 0;
|
||||
}
|
||||
break;
|
||||
case IBEX_UART_FIFO_STATUS:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: fifo_status is read only\n", __func__);
|
||||
break;
|
||||
|
||||
case IBEX_UART_OVRD:
|
||||
s->uart_ovrd = value;
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: ovrd is not supported\n", __func__);
|
||||
break;
|
||||
case IBEX_UART_VAL:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: val is read only\n", __func__);
|
||||
break;
|
||||
case IBEX_UART_TIMEOUT_CTRL:
|
||||
s->uart_timeout_ctrl = value;
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: timeout_ctrl is not supported\n", __func__);
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
|
||||
}
|
||||
}
|
||||
|
||||
static void fifo_trigger_update(void *opaque)
|
||||
{
|
||||
IbexUartState *s = opaque;
|
||||
|
||||
if (s->uart_ctrl & UART_CTRL_TX_ENABLE) {
|
||||
ibex_uart_xmit(NULL, G_IO_OUT, s);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ibex_uart_ops = {
|
||||
.read = ibex_uart_read,
|
||||
.write = ibex_uart_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.impl.min_access_size = 4,
|
||||
.impl.max_access_size = 4,
|
||||
};
|
||||
|
||||
static int ibex_uart_post_load(void *opaque, int version_id)
|
||||
{
|
||||
IbexUartState *s = opaque;
|
||||
|
||||
ibex_uart_update_irqs(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_ibex_uart = {
|
||||
.name = TYPE_IBEX_UART,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.post_load = ibex_uart_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8_ARRAY(tx_fifo, IbexUartState,
|
||||
IBEX_UART_TX_FIFO_SIZE),
|
||||
VMSTATE_UINT32(tx_level, IbexUartState),
|
||||
VMSTATE_UINT64(char_tx_time, IbexUartState),
|
||||
VMSTATE_TIMER_PTR(fifo_trigger_handle, IbexUartState),
|
||||
VMSTATE_UINT32(uart_intr_state, IbexUartState),
|
||||
VMSTATE_UINT32(uart_intr_enable, IbexUartState),
|
||||
VMSTATE_UINT32(uart_ctrl, IbexUartState),
|
||||
VMSTATE_UINT32(uart_status, IbexUartState),
|
||||
VMSTATE_UINT32(uart_rdata, IbexUartState),
|
||||
VMSTATE_UINT32(uart_fifo_ctrl, IbexUartState),
|
||||
VMSTATE_UINT32(uart_fifo_status, IbexUartState),
|
||||
VMSTATE_UINT32(uart_ovrd, IbexUartState),
|
||||
VMSTATE_UINT32(uart_val, IbexUartState),
|
||||
VMSTATE_UINT32(uart_timeout_ctrl, IbexUartState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static Property ibex_uart_properties[] = {
|
||||
DEFINE_PROP_CHR("chardev", IbexUartState, chr),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void ibex_uart_init(Object *obj)
|
||||
{
|
||||
IbexUartState *s = IBEX_UART(obj);
|
||||
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->tx_watermark);
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->rx_watermark);
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->tx_empty);
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->rx_overflow);
|
||||
|
||||
memory_region_init_io(&s->mmio, obj, &ibex_uart_ops, s,
|
||||
TYPE_IBEX_UART, 0x400);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
|
||||
}
|
||||
|
||||
static void ibex_uart_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
IbexUartState *s = IBEX_UART(dev);
|
||||
|
||||
s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||
fifo_trigger_update, s);
|
||||
|
||||
qemu_chr_fe_set_handlers(&s->chr, ibex_uart_can_receive,
|
||||
ibex_uart_receive, NULL, NULL,
|
||||
s, NULL, true);
|
||||
}
|
||||
|
||||
static void ibex_uart_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->reset = ibex_uart_reset;
|
||||
dc->realize = ibex_uart_realize;
|
||||
dc->vmsd = &vmstate_ibex_uart;
|
||||
device_class_set_props(dc, ibex_uart_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo ibex_uart_info = {
|
||||
.name = TYPE_IBEX_UART,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IbexUartState),
|
||||
.instance_init = ibex_uart_init,
|
||||
.class_init = ibex_uart_class_init,
|
||||
};
|
||||
|
||||
static void ibex_uart_register_types(void)
|
||||
{
|
||||
type_register_static(&ibex_uart_info);
|
||||
}
|
||||
|
||||
type_init(ibex_uart_register_types)
|
|
@ -49,3 +49,4 @@ obj-$(CONFIG_ARM_GIC) += arm_gicv3_cpuif.o
|
|||
obj-$(CONFIG_MIPS_CPS) += mips_gic.o
|
||||
obj-$(CONFIG_NIOS2) += nios2_iic.o
|
||||
obj-$(CONFIG_OMPIC) += ompic.o
|
||||
obj-$(CONFIG_IBEX) += ibex_plic.o
|
||||
|
|
261
hw/intc/ibex_plic.c
Normal file
261
hw/intc/ibex_plic.c
Normal file
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* QEMU RISC-V lowRISC Ibex PLIC
|
||||
*
|
||||
* Copyright (c) 2020 Western Digital
|
||||
*
|
||||
* Documentation avaliable: https://docs.opentitan.org/hw/ip/rv_plic/doc/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "target/riscv/cpu_bits.h"
|
||||
#include "target/riscv/cpu.h"
|
||||
#include "hw/intc/ibex_plic.h"
|
||||
|
||||
static bool addr_between(uint32_t addr, uint32_t base, uint32_t num)
|
||||
{
|
||||
uint32_t end = base + (num * 0x04);
|
||||
|
||||
if (addr >= base && addr < end) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ibex_plic_irqs_set_pending(IbexPlicState *s, int irq, bool level)
|
||||
{
|
||||
int pending_num = irq / 32;
|
||||
|
||||
s->pending[pending_num] |= level << (irq % 32);
|
||||
}
|
||||
|
||||
static bool ibex_plic_irqs_pending(IbexPlicState *s, uint32_t context)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->pending_num; i++) {
|
||||
uint32_t irq_num = ctz64(s->pending[i]) + (i * 32);
|
||||
|
||||
if (!(s->pending[i] & s->enable[i])) {
|
||||
/* No pending and enabled IRQ */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (s->priority[irq_num] > s->threshold) {
|
||||
if (!s->claim) {
|
||||
s->claim = irq_num;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ibex_plic_update(IbexPlicState *s)
|
||||
{
|
||||
CPUState *cpu;
|
||||
int level, i;
|
||||
|
||||
for (i = 0; i < s->num_cpus; i++) {
|
||||
cpu = qemu_get_cpu(i);
|
||||
|
||||
if (!cpu) {
|
||||
continue;
|
||||
}
|
||||
|
||||
level = ibex_plic_irqs_pending(s, 0);
|
||||
|
||||
riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MEIP, BOOL_TO_MASK(level));
|
||||
}
|
||||
}
|
||||
|
||||
static void ibex_plic_reset(DeviceState *dev)
|
||||
{
|
||||
IbexPlicState *s = IBEX_PLIC(dev);
|
||||
|
||||
s->threshold = 0x00000000;
|
||||
s->claim = 0x00000000;
|
||||
}
|
||||
|
||||
static uint64_t ibex_plic_read(void *opaque, hwaddr addr,
|
||||
unsigned int size)
|
||||
{
|
||||
IbexPlicState *s = opaque;
|
||||
int offset;
|
||||
uint32_t ret = 0;
|
||||
|
||||
if (addr_between(addr, s->pending_base, s->pending_num)) {
|
||||
offset = (addr - s->pending_base) / 4;
|
||||
ret = s->pending[offset];
|
||||
} else if (addr_between(addr, s->source_base, s->source_num)) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: Interrupt source mode not supported\n", __func__);
|
||||
} else if (addr_between(addr, s->priority_base, s->priority_num)) {
|
||||
offset = (addr - s->priority_base) / 4;
|
||||
ret = s->priority[offset];
|
||||
} else if (addr_between(addr, s->enable_base, s->enable_num)) {
|
||||
offset = (addr - s->enable_base) / 4;
|
||||
ret = s->enable[offset];
|
||||
} else if (addr_between(addr, s->threshold_base, 1)) {
|
||||
ret = s->threshold;
|
||||
} else if (addr_between(addr, s->claim_base, 1)) {
|
||||
int pending_num = s->claim / 32;
|
||||
s->pending[pending_num] &= ~(1 << (s->claim % 32));
|
||||
|
||||
ret = s->claim;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ibex_plic_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned int size)
|
||||
{
|
||||
IbexPlicState *s = opaque;
|
||||
|
||||
if (addr_between(addr, s->pending_base, s->pending_num)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Pending registers are read only\n", __func__);
|
||||
} else if (addr_between(addr, s->source_base, s->source_num)) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: Interrupt source mode not supported\n", __func__);
|
||||
} else if (addr_between(addr, s->priority_base, s->priority_num)) {
|
||||
uint32_t irq = ((addr - s->priority_base) >> 2) + 1;
|
||||
s->priority[irq] = value & 7;
|
||||
} else if (addr_between(addr, s->enable_base, s->enable_num)) {
|
||||
uint32_t enable_reg = (addr - s->enable_base) / 4;
|
||||
|
||||
s->enable[enable_reg] = value;
|
||||
} else if (addr_between(addr, s->threshold_base, 1)) {
|
||||
s->threshold = value & 3;
|
||||
} else if (addr_between(addr, s->claim_base, 1)) {
|
||||
if (s->claim == value) {
|
||||
/* Interrupt was completed */
|
||||
s->claim = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ibex_plic_update(s);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ibex_plic_ops = {
|
||||
.read = ibex_plic_read,
|
||||
.write = ibex_plic_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4
|
||||
}
|
||||
};
|
||||
|
||||
static void ibex_plic_irq_request(void *opaque, int irq, int level)
|
||||
{
|
||||
IbexPlicState *s = opaque;
|
||||
|
||||
ibex_plic_irqs_set_pending(s, irq, level > 0);
|
||||
ibex_plic_update(s);
|
||||
}
|
||||
|
||||
static Property ibex_plic_properties[] = {
|
||||
DEFINE_PROP_UINT32("num-cpus", IbexPlicState, num_cpus, 1),
|
||||
DEFINE_PROP_UINT32("num-sources", IbexPlicState, num_sources, 80),
|
||||
|
||||
DEFINE_PROP_UINT32("pending-base", IbexPlicState, pending_base, 0),
|
||||
DEFINE_PROP_UINT32("pending-num", IbexPlicState, pending_num, 3),
|
||||
|
||||
DEFINE_PROP_UINT32("source-base", IbexPlicState, source_base, 0x0c),
|
||||
DEFINE_PROP_UINT32("source-num", IbexPlicState, source_num, 3),
|
||||
|
||||
DEFINE_PROP_UINT32("priority-base", IbexPlicState, priority_base, 0x18),
|
||||
DEFINE_PROP_UINT32("priority-num", IbexPlicState, priority_num, 80),
|
||||
|
||||
DEFINE_PROP_UINT32("enable-base", IbexPlicState, enable_base, 0x200),
|
||||
DEFINE_PROP_UINT32("enable-num", IbexPlicState, enable_num, 3),
|
||||
|
||||
DEFINE_PROP_UINT32("threshold-base", IbexPlicState, threshold_base, 0x20c),
|
||||
|
||||
DEFINE_PROP_UINT32("claim-base", IbexPlicState, claim_base, 0x210),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void ibex_plic_init(Object *obj)
|
||||
{
|
||||
IbexPlicState *s = IBEX_PLIC(obj);
|
||||
|
||||
memory_region_init_io(&s->mmio, obj, &ibex_plic_ops, s,
|
||||
TYPE_IBEX_PLIC, 0x400);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
|
||||
}
|
||||
|
||||
static void ibex_plic_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
IbexPlicState *s = IBEX_PLIC(dev);
|
||||
int i;
|
||||
|
||||
s->pending = g_new0(uint32_t, s->pending_num);
|
||||
s->source = g_new0(uint32_t, s->source_num);
|
||||
s->priority = g_new0(uint32_t, s->priority_num);
|
||||
s->enable = g_new0(uint32_t, s->enable_num);
|
||||
|
||||
qdev_init_gpio_in(dev, ibex_plic_irq_request, s->num_sources);
|
||||
|
||||
/*
|
||||
* We can't allow the supervisor to control SEIP as this would allow the
|
||||
* supervisor to clear a pending external interrupt which will result in
|
||||
* a lost interrupt in the case a PLIC is attached. The SEIP bit must be
|
||||
* hardware controlled when a PLIC is attached.
|
||||
*/
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
unsigned int smp_cpus = ms->smp.cpus;
|
||||
for (i = 0; i < smp_cpus; i++) {
|
||||
RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
|
||||
if (riscv_cpu_claim_interrupts(cpu, MIP_SEIP) < 0) {
|
||||
error_report("SEIP already claimed");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
msi_nonbroken = true;
|
||||
}
|
||||
|
||||
static void ibex_plic_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->reset = ibex_plic_reset;
|
||||
device_class_set_props(dc, ibex_plic_properties);
|
||||
dc->realize = ibex_plic_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo ibex_plic_info = {
|
||||
.name = TYPE_IBEX_PLIC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IbexPlicState),
|
||||
.instance_init = ibex_plic_init,
|
||||
.class_init = ibex_plic_class_init,
|
||||
};
|
||||
|
||||
static void ibex_plic_register_types(void)
|
||||
{
|
||||
type_register_static(&ibex_plic_info);
|
||||
}
|
||||
|
||||
type_init(ibex_plic_register_types)
|
|
@ -4,6 +4,9 @@ config HTIF
|
|||
config HART
|
||||
bool
|
||||
|
||||
config IBEX
|
||||
bool
|
||||
|
||||
config SIFIVE
|
||||
bool
|
||||
select MSI_NONBROKEN
|
||||
|
@ -29,6 +32,7 @@ config SPIKE
|
|||
|
||||
config OPENTITAN
|
||||
bool
|
||||
select IBEX
|
||||
select HART
|
||||
select UNIMP
|
||||
|
||||
|
|
|
@ -25,12 +25,14 @@
|
|||
#include "hw/misc/unimp.h"
|
||||
#include "hw/riscv/boot.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/units.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
static const struct MemmapEntry {
|
||||
hwaddr base;
|
||||
hwaddr size;
|
||||
} ibex_memmap[] = {
|
||||
[IBEX_ROM] = { 0x00008000, 0xc000 },
|
||||
[IBEX_ROM] = { 0x00008000, 16 * KiB },
|
||||
[IBEX_RAM] = { 0x10000000, 0x10000 },
|
||||
[IBEX_FLASH] = { 0x20000000, 0x80000 },
|
||||
[IBEX_UART] = { 0x40000000, 0x10000 },
|
||||
|
@ -51,7 +53,7 @@ static const struct MemmapEntry {
|
|||
[IBEX_PADCTRL] = { 0x40160000, 0x10000 }
|
||||
};
|
||||
|
||||
static void riscv_opentitan_init(MachineState *machine)
|
||||
static void opentitan_board_init(MachineState *machine)
|
||||
{
|
||||
const struct MemmapEntry *memmap = ibex_memmap;
|
||||
OpenTitanState *s = g_new0(OpenTitanState, 1);
|
||||
|
@ -68,7 +70,6 @@ static void riscv_opentitan_init(MachineState *machine)
|
|||
memory_region_add_subregion(sys_mem,
|
||||
memmap[IBEX_RAM].base, main_mem);
|
||||
|
||||
|
||||
if (machine->firmware) {
|
||||
riscv_load_firmware(machine->firmware, memmap[IBEX_RAM].base, NULL);
|
||||
}
|
||||
|
@ -78,29 +79,34 @@ static void riscv_opentitan_init(MachineState *machine)
|
|||
}
|
||||
}
|
||||
|
||||
static void riscv_opentitan_machine_init(MachineClass *mc)
|
||||
static void opentitan_machine_init(MachineClass *mc)
|
||||
{
|
||||
mc->desc = "RISC-V Board compatible with OpenTitan";
|
||||
mc->init = riscv_opentitan_init;
|
||||
mc->init = opentitan_board_init;
|
||||
mc->max_cpus = 1;
|
||||
mc->default_cpu_type = TYPE_RISCV_CPU_IBEX;
|
||||
}
|
||||
|
||||
DEFINE_MACHINE("opentitan", riscv_opentitan_machine_init)
|
||||
DEFINE_MACHINE("opentitan", opentitan_machine_init)
|
||||
|
||||
static void riscv_lowrisc_ibex_soc_init(Object *obj)
|
||||
static void lowrisc_ibex_soc_init(Object *obj)
|
||||
{
|
||||
LowRISCIbexSoCState *s = RISCV_IBEX_SOC(obj);
|
||||
|
||||
object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY);
|
||||
|
||||
object_initialize_child(obj, "plic", &s->plic, TYPE_IBEX_PLIC);
|
||||
|
||||
object_initialize_child(obj, "uart", &s->uart, TYPE_IBEX_UART);
|
||||
}
|
||||
|
||||
static void riscv_lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
{
|
||||
const struct MemmapEntry *memmap = ibex_memmap;
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
LowRISCIbexSoCState *s = RISCV_IBEX_SOC(dev_soc);
|
||||
MemoryRegion *sys_mem = get_system_memory();
|
||||
Error *err = NULL;
|
||||
|
||||
object_property_set_str(OBJECT(&s->cpus), ms->cpu_type, "cpu-type",
|
||||
&error_abort);
|
||||
|
@ -120,8 +126,35 @@ static void riscv_lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp)
|
|||
memory_region_add_subregion(sys_mem, memmap[IBEX_FLASH].base,
|
||||
&s->flash_mem);
|
||||
|
||||
create_unimplemented_device("riscv.lowrisc.ibex.uart",
|
||||
memmap[IBEX_UART].base, memmap[IBEX_UART].size);
|
||||
/* PLIC */
|
||||
sysbus_realize(SYS_BUS_DEVICE(&s->plic), &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->plic), 0, memmap[IBEX_PLIC].base);
|
||||
|
||||
/* UART */
|
||||
qdev_prop_set_chr(DEVICE(&(s->uart)), "chardev", serial_hd(0));
|
||||
sysbus_realize(SYS_BUS_DEVICE(&s->uart), &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart), 0, memmap[IBEX_UART].base);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart),
|
||||
0, qdev_get_gpio_in(DEVICE(&s->plic),
|
||||
IBEX_UART_TX_WATERMARK_IRQ));
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart),
|
||||
1, qdev_get_gpio_in(DEVICE(&s->plic),
|
||||
IBEX_UART_RX_WATERMARK_IRQ));
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart),
|
||||
2, qdev_get_gpio_in(DEVICE(&s->plic),
|
||||
IBEX_UART_TX_EMPTY_IRQ));
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart),
|
||||
3, qdev_get_gpio_in(DEVICE(&s->plic),
|
||||
IBEX_UART_RX_OVERFLOW_IRQ));
|
||||
|
||||
create_unimplemented_device("riscv.lowrisc.ibex.gpio",
|
||||
memmap[IBEX_GPIO].base, memmap[IBEX_GPIO].size);
|
||||
create_unimplemented_device("riscv.lowrisc.ibex.spi",
|
||||
|
@ -140,8 +173,6 @@ static void riscv_lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp)
|
|||
memmap[IBEX_AES].base, memmap[IBEX_AES].size);
|
||||
create_unimplemented_device("riscv.lowrisc.ibex.hmac",
|
||||
memmap[IBEX_HMAC].base, memmap[IBEX_HMAC].size);
|
||||
create_unimplemented_device("riscv.lowrisc.ibex.plic",
|
||||
memmap[IBEX_PLIC].base, memmap[IBEX_PLIC].size);
|
||||
create_unimplemented_device("riscv.lowrisc.ibex.pinmux",
|
||||
memmap[IBEX_PINMUX].base, memmap[IBEX_PINMUX].size);
|
||||
create_unimplemented_device("riscv.lowrisc.ibex.alert_handler",
|
||||
|
@ -154,26 +185,26 @@ static void riscv_lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp)
|
|||
memmap[IBEX_PADCTRL].base, memmap[IBEX_PADCTRL].size);
|
||||
}
|
||||
|
||||
static void riscv_lowrisc_ibex_soc_class_init(ObjectClass *oc, void *data)
|
||||
static void lowrisc_ibex_soc_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = riscv_lowrisc_ibex_soc_realize;
|
||||
dc->realize = lowrisc_ibex_soc_realize;
|
||||
/* Reason: Uses serial_hds in realize function, thus can't be used twice */
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
static const TypeInfo riscv_lowrisc_ibex_soc_type_info = {
|
||||
static const TypeInfo lowrisc_ibex_soc_type_info = {
|
||||
.name = TYPE_RISCV_IBEX_SOC,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(LowRISCIbexSoCState),
|
||||
.instance_init = riscv_lowrisc_ibex_soc_init,
|
||||
.class_init = riscv_lowrisc_ibex_soc_class_init,
|
||||
.instance_init = lowrisc_ibex_soc_init,
|
||||
.class_init = lowrisc_ibex_soc_class_init,
|
||||
};
|
||||
|
||||
static void riscv_lowrisc_ibex_soc_register_types(void)
|
||||
static void lowrisc_ibex_soc_register_types(void)
|
||||
{
|
||||
type_register_static(&riscv_lowrisc_ibex_soc_type_info);
|
||||
type_register_static(&lowrisc_ibex_soc_type_info);
|
||||
}
|
||||
|
||||
type_init(riscv_lowrisc_ibex_soc_register_types)
|
||||
type_init(lowrisc_ibex_soc_register_types)
|
||||
|
|
|
@ -75,7 +75,7 @@ static const struct MemmapEntry {
|
|||
[SIFIVE_E_DTIM] = { 0x80000000, 0x4000 }
|
||||
};
|
||||
|
||||
static void riscv_sifive_e_init(MachineState *machine)
|
||||
static void sifive_e_machine_init(MachineState *machine)
|
||||
{
|
||||
const struct MemmapEntry *memmap = sifive_e_memmap;
|
||||
|
||||
|
@ -95,10 +95,16 @@ static void riscv_sifive_e_init(MachineState *machine)
|
|||
memmap[SIFIVE_E_DTIM].base, main_mem);
|
||||
|
||||
/* Mask ROM reset vector */
|
||||
uint32_t reset_vec[2] = {
|
||||
0x204002b7, /* 0x1000: lui t0,0x20400 */
|
||||
0x00028067, /* 0x1004: jr t0 */
|
||||
};
|
||||
uint32_t reset_vec[4];
|
||||
|
||||
if (s->revb) {
|
||||
reset_vec[1] = 0x200102b7; /* 0x1004: lui t0,0x20010 */
|
||||
} else {
|
||||
reset_vec[1] = 0x204002b7; /* 0x1004: lui t0,0x20400 */
|
||||
}
|
||||
reset_vec[2] = 0x00028067; /* 0x1008: jr t0 */
|
||||
|
||||
reset_vec[0] = reset_vec[3] = 0;
|
||||
|
||||
/* copy in the reset vector in little_endian byte order */
|
||||
for (i = 0; i < sizeof(reset_vec) >> 2; i++) {
|
||||
|
@ -112,8 +118,30 @@ static void riscv_sifive_e_init(MachineState *machine)
|
|||
}
|
||||
}
|
||||
|
||||
static bool sifive_e_machine_get_revb(Object *obj, Error **errp)
|
||||
{
|
||||
SiFiveEState *s = RISCV_E_MACHINE(obj);
|
||||
|
||||
return s->revb;
|
||||
}
|
||||
|
||||
static void sifive_e_machine_set_revb(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
SiFiveEState *s = RISCV_E_MACHINE(obj);
|
||||
|
||||
s->revb = value;
|
||||
}
|
||||
|
||||
static void sifive_e_machine_instance_init(Object *obj)
|
||||
{
|
||||
SiFiveEState *s = RISCV_E_MACHINE(obj);
|
||||
|
||||
s->revb = false;
|
||||
object_property_add_bool(obj, "revb", sifive_e_machine_get_revb,
|
||||
sifive_e_machine_set_revb);
|
||||
object_property_set_description(obj, "revb",
|
||||
"Set on to tell QEMU that it should model "
|
||||
"the revB HiFive1 board");
|
||||
}
|
||||
|
||||
static void sifive_e_machine_class_init(ObjectClass *oc, void *data)
|
||||
|
@ -121,7 +149,7 @@ static void sifive_e_machine_class_init(ObjectClass *oc, void *data)
|
|||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
|
||||
mc->desc = "RISC-V Board compatible with SiFive E SDK";
|
||||
mc->init = riscv_sifive_e_init;
|
||||
mc->init = sifive_e_machine_init;
|
||||
mc->max_cpus = 1;
|
||||
mc->default_cpu_type = SIFIVE_E_CPU;
|
||||
}
|
||||
|
@ -141,7 +169,7 @@ static void sifive_e_machine_init_register_types(void)
|
|||
|
||||
type_init(sifive_e_machine_init_register_types)
|
||||
|
||||
static void riscv_sifive_e_soc_init(Object *obj)
|
||||
static void sifive_e_soc_init(Object *obj)
|
||||
{
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
SiFiveESoCState *s = RISCV_E_SOC(obj);
|
||||
|
@ -153,7 +181,7 @@ static void riscv_sifive_e_soc_init(Object *obj)
|
|||
TYPE_SIFIVE_GPIO);
|
||||
}
|
||||
|
||||
static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp)
|
||||
static void sifive_e_soc_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
const struct MemmapEntry *memmap = sifive_e_memmap;
|
||||
|
@ -236,26 +264,26 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp)
|
|||
&s->xip_mem);
|
||||
}
|
||||
|
||||
static void riscv_sifive_e_soc_class_init(ObjectClass *oc, void *data)
|
||||
static void sifive_e_soc_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = riscv_sifive_e_soc_realize;
|
||||
dc->realize = sifive_e_soc_realize;
|
||||
/* Reason: Uses serial_hds in realize function, thus can't be used twice */
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
static const TypeInfo riscv_sifive_e_soc_type_info = {
|
||||
static const TypeInfo sifive_e_soc_type_info = {
|
||||
.name = TYPE_RISCV_E_SOC,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(SiFiveESoCState),
|
||||
.instance_init = riscv_sifive_e_soc_init,
|
||||
.class_init = riscv_sifive_e_soc_class_init,
|
||||
.instance_init = sifive_e_soc_init,
|
||||
.class_init = sifive_e_soc_class_init,
|
||||
};
|
||||
|
||||
static void riscv_sifive_e_soc_register_types(void)
|
||||
static void sifive_e_soc_register_types(void)
|
||||
{
|
||||
type_register_static(&riscv_sifive_e_soc_type_info);
|
||||
type_register_static(&sifive_e_soc_type_info);
|
||||
}
|
||||
|
||||
type_init(riscv_sifive_e_soc_register_types)
|
||||
type_init(sifive_e_soc_register_types)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* sifive System-on-Chip general purpose input/output register definition
|
||||
* SiFive System-on-Chip general purpose input/output register definition
|
||||
*
|
||||
* Copyright 2019 AdaCore
|
||||
*
|
||||
|
@ -14,13 +14,13 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/riscv/sifive_gpio.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "trace.h"
|
||||
|
||||
static void update_output_irq(SIFIVEGPIOState *s)
|
||||
{
|
||||
|
||||
uint32_t pending;
|
||||
uint32_t pin;
|
||||
|
||||
|
@ -29,7 +29,7 @@ static void update_output_irq(SIFIVEGPIOState *s)
|
|||
pending |= s->rise_ip & s->rise_ie;
|
||||
pending |= s->fall_ip & s->fall_ie;
|
||||
|
||||
for (int i = 0; i < SIFIVE_GPIO_PINS; i++) {
|
||||
for (int i = 0; i < s->ngpio; i++) {
|
||||
pin = 1 << i;
|
||||
qemu_set_irq(s->irq[i], (pending & pin) != 0);
|
||||
trace_sifive_gpio_update_output_irq(i, (pending & pin) != 0);
|
||||
|
@ -42,7 +42,7 @@ static void update_state(SIFIVEGPIOState *s)
|
|||
bool prev_ival, in, in_mask, port, out_xor, pull, output_en, input_en,
|
||||
rise_ip, fall_ip, low_ip, high_ip, oval, actual_value, ival;
|
||||
|
||||
for (i = 0; i < SIFIVE_GPIO_PINS; i++) {
|
||||
for (i = 0; i < s->ngpio; i++) {
|
||||
|
||||
prev_ival = extract32(s->value, i, 1);
|
||||
in = extract32(s->in, i, 1);
|
||||
|
@ -76,7 +76,9 @@ static void update_state(SIFIVEGPIOState *s)
|
|||
actual_value = pull;
|
||||
}
|
||||
|
||||
qemu_set_irq(s->output[i], actual_value);
|
||||
if (output_en) {
|
||||
qemu_set_irq(s->output[i], actual_value);
|
||||
}
|
||||
|
||||
/* Input value */
|
||||
ival = input_en && actual_value;
|
||||
|
@ -186,7 +188,7 @@ static uint64_t sifive_gpio_read(void *opaque, hwaddr offset, unsigned int size)
|
|||
}
|
||||
|
||||
static void sifive_gpio_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned int size)
|
||||
uint64_t value, unsigned int size)
|
||||
{
|
||||
SIFIVEGPIOState *s = SIFIVE_GPIO(opaque);
|
||||
|
||||
|
@ -318,7 +320,6 @@ static void sifive_gpio_reset(DeviceState *dev)
|
|||
s->out_xor = 0;
|
||||
s->in = 0;
|
||||
s->in_mask = 0;
|
||||
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_sifive_gpio = {
|
||||
|
@ -342,43 +343,49 @@ static const VMStateDescription vmstate_sifive_gpio = {
|
|||
VMSTATE_UINT32(iof_en, SIFIVEGPIOState),
|
||||
VMSTATE_UINT32(iof_sel, SIFIVEGPIOState),
|
||||
VMSTATE_UINT32(out_xor, SIFIVEGPIOState),
|
||||
VMSTATE_UINT32(in, SIFIVEGPIOState),
|
||||
VMSTATE_UINT32(in_mask, SIFIVEGPIOState),
|
||||
VMSTATE_UINT32(in, SIFIVEGPIOState),
|
||||
VMSTATE_UINT32(in_mask, SIFIVEGPIOState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void sifive_gpio_init(Object *obj)
|
||||
static Property sifive_gpio_properties[] = {
|
||||
DEFINE_PROP_UINT32("ngpio", SIFIVEGPIOState, ngpio, SIFIVE_GPIO_PINS),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void sifive_gpio_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
SIFIVEGPIOState *s = SIFIVE_GPIO(obj);
|
||||
SIFIVEGPIOState *s = SIFIVE_GPIO(dev);
|
||||
|
||||
memory_region_init_io(&s->mmio, obj, &gpio_ops, s,
|
||||
memory_region_init_io(&s->mmio, OBJECT(dev), &gpio_ops, s,
|
||||
TYPE_SIFIVE_GPIO, SIFIVE_GPIO_SIZE);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
|
||||
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
|
||||
|
||||
for (int i = 0; i < SIFIVE_GPIO_PINS; i++) {
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq[i]);
|
||||
for (int i = 0; i < s->ngpio; i++) {
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]);
|
||||
}
|
||||
|
||||
qdev_init_gpio_in(DEVICE(s), sifive_gpio_set, SIFIVE_GPIO_PINS);
|
||||
qdev_init_gpio_out(DEVICE(s), s->output, SIFIVE_GPIO_PINS);
|
||||
qdev_init_gpio_in(DEVICE(s), sifive_gpio_set, s->ngpio);
|
||||
qdev_init_gpio_out(DEVICE(s), s->output, s->ngpio);
|
||||
}
|
||||
|
||||
static void sifive_gpio_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
device_class_set_props(dc, sifive_gpio_properties);
|
||||
dc->vmsd = &vmstate_sifive_gpio;
|
||||
dc->realize = sifive_gpio_realize;
|
||||
dc->reset = sifive_gpio_reset;
|
||||
dc->desc = "sifive GPIO";
|
||||
dc->desc = "SiFive GPIO";
|
||||
}
|
||||
|
||||
static const TypeInfo sifive_gpio_info = {
|
||||
.name = TYPE_SIFIVE_GPIO,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(SIFIVEGPIOState),
|
||||
.instance_init = sifive_gpio_init,
|
||||
.class_init = sifive_gpio_class_init
|
||||
};
|
||||
|
||||
|
|
|
@ -11,8 +11,9 @@
|
|||
* 1) CLINT (Core Level Interruptor)
|
||||
* 2) PLIC (Platform Level Interrupt Controller)
|
||||
* 3) PRCI (Power, Reset, Clock, Interrupt)
|
||||
* 4) OTP (One-Time Programmable) memory with stored serial number
|
||||
* 5) GEM (Gigabit Ethernet Controller) and management block
|
||||
* 4) GPIO (General Purpose Input/Output Controller)
|
||||
* 5) OTP (One-Time Programmable) memory with stored serial number
|
||||
* 6) GEM (Gigabit Ethernet Controller) and management block
|
||||
*
|
||||
* This board currently generates devicetree dynamically that indicates at least
|
||||
* two harts and up to five harts.
|
||||
|
@ -36,6 +37,7 @@
|
|||
#include "qapi/error.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/char/serial.h"
|
||||
|
@ -52,6 +54,7 @@
|
|||
#include "net/eth.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "exec/address-spaces.h"
|
||||
|
||||
|
@ -75,11 +78,13 @@ static const struct MemmapEntry {
|
|||
[SIFIVE_U_PRCI] = { 0x10000000, 0x1000 },
|
||||
[SIFIVE_U_UART0] = { 0x10010000, 0x1000 },
|
||||
[SIFIVE_U_UART1] = { 0x10011000, 0x1000 },
|
||||
[SIFIVE_U_GPIO] = { 0x10060000, 0x1000 },
|
||||
[SIFIVE_U_OTP] = { 0x10070000, 0x1000 },
|
||||
[SIFIVE_U_FLASH0] = { 0x20000000, 0x10000000 },
|
||||
[SIFIVE_U_DRAM] = { 0x80000000, 0x0 },
|
||||
[SIFIVE_U_GEM] = { 0x10090000, 0x2000 },
|
||||
[SIFIVE_U_GEM_MGMT] = { 0x100a0000, 0x1000 },
|
||||
[SIFIVE_U_DMC] = { 0x100b0000, 0x10000 },
|
||||
[SIFIVE_U_FLASH0] = { 0x20000000, 0x10000000 },
|
||||
[SIFIVE_U_DRAM] = { 0x80000000, 0x0 },
|
||||
};
|
||||
|
||||
#define OTP_SERIAL 1
|
||||
|
@ -94,7 +99,7 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
|
|||
uint32_t *cells;
|
||||
char *nodename;
|
||||
char ethclk_names[] = "pclk\0hclk";
|
||||
uint32_t plic_phandle, prci_phandle, phandle = 1;
|
||||
uint32_t plic_phandle, prci_phandle, gpio_phandle, phandle = 1;
|
||||
uint32_t hfclk_phandle, rtcclk_phandle, phy_phandle;
|
||||
|
||||
fdt = s->fdt = create_device_tree(&s->fdt_size);
|
||||
|
@ -207,6 +212,17 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
|
|||
g_free(cells);
|
||||
g_free(nodename);
|
||||
|
||||
nodename = g_strdup_printf("/soc/otp@%lx",
|
||||
(long)memmap[SIFIVE_U_OTP].base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "fuse-count", SIFIVE_U_OTP_REG_SIZE);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "reg",
|
||||
0x0, memmap[SIFIVE_U_OTP].base,
|
||||
0x0, memmap[SIFIVE_U_OTP].size);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible",
|
||||
"sifive,fu540-c000-otp");
|
||||
g_free(nodename);
|
||||
|
||||
prci_phandle = phandle++;
|
||||
nodename = g_strdup_printf("/soc/clock-controller@%lx",
|
||||
(long)memmap[SIFIVE_U_PRCI].base);
|
||||
|
@ -257,6 +273,36 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
|
|||
g_free(cells);
|
||||
g_free(nodename);
|
||||
|
||||
gpio_phandle = phandle++;
|
||||
nodename = g_strdup_printf("/soc/gpio@%lx",
|
||||
(long)memmap[SIFIVE_U_GPIO].base);
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "phandle", gpio_phandle);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "clocks",
|
||||
prci_phandle, PRCI_CLK_TLCLK);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 2);
|
||||
qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "#gpio-cells", 2);
|
||||
qemu_fdt_setprop(fdt, nodename, "gpio-controller", NULL, 0);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "reg",
|
||||
0x0, memmap[SIFIVE_U_GPIO].base,
|
||||
0x0, memmap[SIFIVE_U_GPIO].size);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "interrupts", SIFIVE_U_GPIO_IRQ0,
|
||||
SIFIVE_U_GPIO_IRQ1, SIFIVE_U_GPIO_IRQ2, SIFIVE_U_GPIO_IRQ3,
|
||||
SIFIVE_U_GPIO_IRQ4, SIFIVE_U_GPIO_IRQ5, SIFIVE_U_GPIO_IRQ6,
|
||||
SIFIVE_U_GPIO_IRQ7, SIFIVE_U_GPIO_IRQ8, SIFIVE_U_GPIO_IRQ9,
|
||||
SIFIVE_U_GPIO_IRQ10, SIFIVE_U_GPIO_IRQ11, SIFIVE_U_GPIO_IRQ12,
|
||||
SIFIVE_U_GPIO_IRQ13, SIFIVE_U_GPIO_IRQ14, SIFIVE_U_GPIO_IRQ15);
|
||||
qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,gpio0");
|
||||
g_free(nodename);
|
||||
|
||||
nodename = g_strdup_printf("/gpio-restart");
|
||||
qemu_fdt_add_subnode(fdt, nodename);
|
||||
qemu_fdt_setprop_cells(fdt, nodename, "gpios", gpio_phandle, 10, 1);
|
||||
qemu_fdt_setprop_string(fdt, nodename, "compatible", "gpio-restart");
|
||||
g_free(nodename);
|
||||
|
||||
phy_phandle = phandle++;
|
||||
nodename = g_strdup_printf("/soc/ethernet@%lx",
|
||||
(long)memmap[SIFIVE_U_GEM].base);
|
||||
|
@ -317,6 +363,14 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
|
|||
g_free(nodename);
|
||||
}
|
||||
|
||||
static void sifive_u_machine_reset(void *opaque, int n, int level)
|
||||
{
|
||||
/* gpio pin active low triggers reset */
|
||||
if (!level) {
|
||||
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
|
||||
}
|
||||
}
|
||||
|
||||
static void sifive_u_machine_init(MachineState *machine)
|
||||
{
|
||||
const struct MemmapEntry *memmap = sifive_u_memmap;
|
||||
|
@ -345,11 +399,41 @@ static void sifive_u_machine_init(MachineState *machine)
|
|||
memory_region_add_subregion(system_memory, memmap[SIFIVE_U_FLASH0].base,
|
||||
flash0);
|
||||
|
||||
/* register gpio-restart */
|
||||
qdev_connect_gpio_out(DEVICE(&(s->soc.gpio)), 10,
|
||||
qemu_allocate_irq(sifive_u_machine_reset, NULL, 0));
|
||||
|
||||
/* create device tree */
|
||||
create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
|
||||
|
||||
riscv_find_and_load_firmware(machine, BIOS_FILENAME,
|
||||
memmap[SIFIVE_U_DRAM].base, NULL);
|
||||
if (s->start_in_flash) {
|
||||
/*
|
||||
* If start_in_flash property is given, assign s->msel to a value
|
||||
* that representing booting from QSPI0 memory-mapped flash.
|
||||
*
|
||||
* This also means that when both start_in_flash and msel properties
|
||||
* are given, start_in_flash takes the precedence over msel.
|
||||
*
|
||||
* Note this is to keep backward compatibility not to break existing
|
||||
* users that use start_in_flash property.
|
||||
*/
|
||||
s->msel = MSEL_MEMMAP_QSPI0_FLASH;
|
||||
}
|
||||
|
||||
switch (s->msel) {
|
||||
case MSEL_MEMMAP_QSPI0_FLASH:
|
||||
start_addr = memmap[SIFIVE_U_FLASH0].base;
|
||||
break;
|
||||
case MSEL_L2LIM_QSPI0_FLASH:
|
||||
case MSEL_L2LIM_QSPI2_SD:
|
||||
start_addr = memmap[SIFIVE_U_L2LIM].base;
|
||||
break;
|
||||
default:
|
||||
start_addr = memmap[SIFIVE_U_DRAM].base;
|
||||
break;
|
||||
}
|
||||
|
||||
riscv_find_and_load_firmware(machine, BIOS_FILENAME, start_addr, NULL);
|
||||
|
||||
if (machine->kernel_filename) {
|
||||
uint64_t kernel_entry = riscv_load_kernel(machine->kernel_filename,
|
||||
|
@ -367,24 +451,20 @@ static void sifive_u_machine_init(MachineState *machine)
|
|||
}
|
||||
}
|
||||
|
||||
if (s->start_in_flash) {
|
||||
start_addr = memmap[SIFIVE_U_FLASH0].base;
|
||||
}
|
||||
|
||||
/* reset vector */
|
||||
uint32_t reset_vec[8] = {
|
||||
s->msel, /* MSEL pin state */
|
||||
0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */
|
||||
0x02028593, /* addi a1, t0, %pcrel_lo(1b) */
|
||||
0x01c28593, /* addi a1, t0, %pcrel_lo(1b) */
|
||||
0xf1402573, /* csrr a0, mhartid */
|
||||
#if defined(TARGET_RISCV32)
|
||||
0x0182a283, /* lw t0, 24(t0) */
|
||||
#elif defined(TARGET_RISCV64)
|
||||
0x0182b283, /* ld t0, 24(t0) */
|
||||
0x0182e283, /* lwu t0, 24(t0) */
|
||||
#endif
|
||||
0x00028067, /* jr t0 */
|
||||
0x00000000,
|
||||
start_addr, /* start: .dword */
|
||||
0x00000000,
|
||||
/* dtb: */
|
||||
};
|
||||
|
||||
|
@ -421,14 +501,16 @@ static void sifive_u_machine_set_start_in_flash(Object *obj, bool value, Error *
|
|||
s->start_in_flash = value;
|
||||
}
|
||||
|
||||
static void sifive_u_machine_get_serial(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
static void sifive_u_machine_get_uint32_prop(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
visit_type_uint32(v, name, (uint32_t *)opaque, errp);
|
||||
}
|
||||
|
||||
static void sifive_u_machine_set_serial(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
static void sifive_u_machine_set_uint32_prop(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
visit_type_uint32(v, name, (uint32_t *)opaque, errp);
|
||||
}
|
||||
|
@ -443,12 +525,20 @@ static void sifive_u_machine_instance_init(Object *obj)
|
|||
sifive_u_machine_set_start_in_flash);
|
||||
object_property_set_description(obj, "start-in-flash",
|
||||
"Set on to tell QEMU's ROM to jump to "
|
||||
"flash. Otherwise QEMU will jump to DRAM");
|
||||
"flash. Otherwise QEMU will jump to DRAM "
|
||||
"or L2LIM depending on the msel value");
|
||||
|
||||
s->msel = 0;
|
||||
object_property_add(obj, "msel", "uint32",
|
||||
sifive_u_machine_get_uint32_prop,
|
||||
sifive_u_machine_set_uint32_prop, NULL, &s->msel);
|
||||
object_property_set_description(obj, "msel",
|
||||
"Mode Select (MSEL[3:0]) pin state");
|
||||
|
||||
s->serial = OTP_SERIAL;
|
||||
object_property_add(obj, "serial", "uint32",
|
||||
sifive_u_machine_get_serial,
|
||||
sifive_u_machine_set_serial, NULL, &s->serial);
|
||||
sifive_u_machine_get_uint32_prop,
|
||||
sifive_u_machine_set_uint32_prop, NULL, &s->serial);
|
||||
object_property_set_description(obj, "serial", "Board serial number");
|
||||
}
|
||||
|
||||
|
@ -504,6 +594,7 @@ static void sifive_u_soc_instance_init(Object *obj)
|
|||
object_initialize_child(obj, "prci", &s->prci, TYPE_SIFIVE_U_PRCI);
|
||||
object_initialize_child(obj, "otp", &s->otp, TYPE_SIFIVE_U_OTP);
|
||||
object_initialize_child(obj, "gem", &s->gem, TYPE_CADENCE_GEM);
|
||||
object_initialize_child(obj, "gpio", &s->gpio, TYPE_SIFIVE_GPIO);
|
||||
}
|
||||
|
||||
static void sifive_u_soc_realize(DeviceState *dev, Error **errp)
|
||||
|
@ -514,7 +605,6 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp)
|
|||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *l2lim_mem = g_new(MemoryRegion, 1);
|
||||
qemu_irq plic_gpios[SIFIVE_U_PLIC_NUM_SOURCES];
|
||||
char *plic_hart_config;
|
||||
size_t plic_hart_config_len;
|
||||
int i;
|
||||
|
@ -590,14 +680,24 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp)
|
|||
sysbus_realize(SYS_BUS_DEVICE(&s->prci), &err);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->prci), 0, memmap[SIFIVE_U_PRCI].base);
|
||||
|
||||
qdev_prop_set_uint32(DEVICE(&s->gpio), "ngpio", 16);
|
||||
sysbus_realize(SYS_BUS_DEVICE(&s->gpio), &err);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, memmap[SIFIVE_U_GPIO].base);
|
||||
|
||||
/* Pass all GPIOs to the SOC layer so they are available to the board */
|
||||
qdev_pass_gpios(DEVICE(&s->gpio), dev, NULL);
|
||||
|
||||
/* Connect GPIO interrupts to the PLIC */
|
||||
for (i = 0; i < 16; i++) {
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), i,
|
||||
qdev_get_gpio_in(DEVICE(s->plic),
|
||||
SIFIVE_U_GPIO_IRQ0 + i));
|
||||
}
|
||||
|
||||
qdev_prop_set_uint32(DEVICE(&s->otp), "serial", s->serial);
|
||||
sysbus_realize(SYS_BUS_DEVICE(&s->otp), &err);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->otp), 0, memmap[SIFIVE_U_OTP].base);
|
||||
|
||||
for (i = 0; i < SIFIVE_U_PLIC_NUM_SOURCES; i++) {
|
||||
plic_gpios[i] = qdev_get_gpio_in(DEVICE(s->plic), i);
|
||||
}
|
||||
|
||||
if (nd->used) {
|
||||
qemu_check_nic_model(nd, TYPE_CADENCE_GEM);
|
||||
qdev_set_nic_properties(DEVICE(&s->gem), nd);
|
||||
|
@ -611,10 +711,13 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp)
|
|||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem), 0, memmap[SIFIVE_U_GEM].base);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem), 0,
|
||||
plic_gpios[SIFIVE_U_GEM_IRQ]);
|
||||
qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_GEM_IRQ));
|
||||
|
||||
create_unimplemented_device("riscv.sifive.u.gem-mgmt",
|
||||
memmap[SIFIVE_U_GEM_MGMT].base, memmap[SIFIVE_U_GEM_MGMT].size);
|
||||
|
||||
create_unimplemented_device("riscv.sifive.u.dmc",
|
||||
memmap[SIFIVE_U_DMC].base, memmap[SIFIVE_U_DMC].size);
|
||||
}
|
||||
|
||||
static Property sifive_u_soc_props[] = {
|
||||
|
|
110
include/hw/char/ibex_uart.h
Normal file
110
include/hw/char/ibex_uart.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* QEMU lowRISC Ibex UART device
|
||||
*
|
||||
* Copyright (c) 2020 Western Digital
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef HW_IBEX_UART_H
|
||||
#define HW_IBEX_UART_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "chardev/char-fe.h"
|
||||
#include "qemu/timer.h"
|
||||
|
||||
#define IBEX_UART_INTR_STATE 0x00
|
||||
#define INTR_STATE_TX_WATERMARK (1 << 0)
|
||||
#define INTR_STATE_RX_WATERMARK (1 << 1)
|
||||
#define INTR_STATE_TX_EMPTY (1 << 2)
|
||||
#define INTR_STATE_RX_OVERFLOW (1 << 3)
|
||||
#define IBEX_UART_INTR_ENABLE 0x04
|
||||
#define IBEX_UART_INTR_TEST 0x08
|
||||
|
||||
#define IBEX_UART_CTRL 0x0c
|
||||
#define UART_CTRL_TX_ENABLE (1 << 0)
|
||||
#define UART_CTRL_RX_ENABLE (1 << 1)
|
||||
#define UART_CTRL_NF (1 << 2)
|
||||
#define UART_CTRL_SLPBK (1 << 4)
|
||||
#define UART_CTRL_LLPBK (1 << 5)
|
||||
#define UART_CTRL_PARITY_EN (1 << 6)
|
||||
#define UART_CTRL_PARITY_ODD (1 << 7)
|
||||
#define UART_CTRL_RXBLVL (3 << 8)
|
||||
#define UART_CTRL_NCO (0xFFFF << 16)
|
||||
|
||||
#define IBEX_UART_STATUS 0x10
|
||||
#define UART_STATUS_TXFULL (1 << 0)
|
||||
#define UART_STATUS_RXFULL (1 << 1)
|
||||
#define UART_STATUS_TXEMPTY (1 << 2)
|
||||
#define UART_STATUS_RXIDLE (1 << 4)
|
||||
#define UART_STATUS_RXEMPTY (1 << 5)
|
||||
|
||||
#define IBEX_UART_RDATA 0x14
|
||||
#define IBEX_UART_WDATA 0x18
|
||||
|
||||
#define IBEX_UART_FIFO_CTRL 0x1c
|
||||
#define FIFO_CTRL_RXRST (1 << 0)
|
||||
#define FIFO_CTRL_TXRST (1 << 1)
|
||||
#define FIFO_CTRL_RXILVL (7 << 2)
|
||||
#define FIFO_CTRL_RXILVL_SHIFT (2)
|
||||
#define FIFO_CTRL_TXILVL (3 << 5)
|
||||
#define FIFO_CTRL_TXILVL_SHIFT (5)
|
||||
|
||||
#define IBEX_UART_FIFO_STATUS 0x20
|
||||
#define IBEX_UART_OVRD 0x24
|
||||
#define IBEX_UART_VAL 0x28
|
||||
#define IBEX_UART_TIMEOUT_CTRL 0x2c
|
||||
|
||||
#define IBEX_UART_TX_FIFO_SIZE 16
|
||||
|
||||
#define TYPE_IBEX_UART "ibex-uart"
|
||||
#define IBEX_UART(obj) \
|
||||
OBJECT_CHECK(IbexUartState, (obj), TYPE_IBEX_UART)
|
||||
|
||||
typedef struct {
|
||||
/* <private> */
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
/* <public> */
|
||||
MemoryRegion mmio;
|
||||
|
||||
uint8_t tx_fifo[IBEX_UART_TX_FIFO_SIZE];
|
||||
uint32_t tx_level;
|
||||
|
||||
QEMUTimer *fifo_trigger_handle;
|
||||
uint64_t char_tx_time;
|
||||
|
||||
uint32_t uart_intr_state;
|
||||
uint32_t uart_intr_enable;
|
||||
uint32_t uart_ctrl;
|
||||
uint32_t uart_status;
|
||||
uint32_t uart_rdata;
|
||||
uint32_t uart_fifo_ctrl;
|
||||
uint32_t uart_fifo_status;
|
||||
uint32_t uart_ovrd;
|
||||
uint32_t uart_val;
|
||||
uint32_t uart_timeout_ctrl;
|
||||
|
||||
CharBackend chr;
|
||||
qemu_irq tx_watermark;
|
||||
qemu_irq rx_watermark;
|
||||
qemu_irq tx_empty;
|
||||
qemu_irq rx_overflow;
|
||||
} IbexUartState;
|
||||
#endif /* HW_IBEX_UART_H */
|
63
include/hw/intc/ibex_plic.h
Normal file
63
include/hw/intc/ibex_plic.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* QEMU RISC-V lowRISC Ibex PLIC
|
||||
*
|
||||
* Copyright (c) 2020 Western Digital
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HW_IBEX_PLIC_H
|
||||
#define HW_IBEX_PLIC_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_IBEX_PLIC "ibex-plic"
|
||||
#define IBEX_PLIC(obj) \
|
||||
OBJECT_CHECK(IbexPlicState, (obj), TYPE_IBEX_PLIC)
|
||||
|
||||
typedef struct IbexPlicState {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
/*< public >*/
|
||||
MemoryRegion mmio;
|
||||
|
||||
uint32_t *pending;
|
||||
uint32_t *source;
|
||||
uint32_t *priority;
|
||||
uint32_t *enable;
|
||||
uint32_t threshold;
|
||||
uint32_t claim;
|
||||
|
||||
/* config */
|
||||
uint32_t num_cpus;
|
||||
uint32_t num_sources;
|
||||
|
||||
uint32_t pending_base;
|
||||
uint32_t pending_num;
|
||||
|
||||
uint32_t source_base;
|
||||
uint32_t source_num;
|
||||
|
||||
uint32_t priority_base;
|
||||
uint32_t priority_num;
|
||||
|
||||
uint32_t enable_base;
|
||||
uint32_t enable_num;
|
||||
|
||||
uint32_t threshold_base;
|
||||
|
||||
uint32_t claim_base;
|
||||
} IbexPlicState;
|
||||
|
||||
#endif /* HW_IBEX_PLIC_H */
|
|
@ -20,6 +20,8 @@
|
|||
#define HW_OPENTITAN_H
|
||||
|
||||
#include "hw/riscv/riscv_hart.h"
|
||||
#include "hw/intc/ibex_plic.h"
|
||||
#include "hw/char/ibex_uart.h"
|
||||
|
||||
#define TYPE_RISCV_IBEX_SOC "riscv.lowrisc.ibex.soc"
|
||||
#define RISCV_IBEX_SOC(obj) \
|
||||
|
@ -31,6 +33,9 @@ typedef struct LowRISCIbexSoCState {
|
|||
|
||||
/*< public >*/
|
||||
RISCVHartArrayState cpus;
|
||||
IbexPlicState plic;
|
||||
IbexUartState uart;
|
||||
|
||||
MemoryRegion flash_mem;
|
||||
MemoryRegion rom;
|
||||
} LowRISCIbexSoCState;
|
||||
|
@ -65,4 +70,15 @@ enum {
|
|||
IBEX_PADCTRL,
|
||||
};
|
||||
|
||||
enum {
|
||||
IBEX_UART_RX_PARITY_ERR_IRQ = 0x28,
|
||||
IBEX_UART_RX_TIMEOUT_IRQ = 0x27,
|
||||
IBEX_UART_RX_BREAK_ERR_IRQ = 0x26,
|
||||
IBEX_UART_RX_FRAME_ERR_IRQ = 0x25,
|
||||
IBEX_UART_RX_OVERFLOW_IRQ = 0x24,
|
||||
IBEX_UART_TX_EMPTY_IRQ = 0x23,
|
||||
IBEX_UART_RX_WATERMARK_IRQ = 0x22,
|
||||
IBEX_UART_TX_WATERMARK_IRQ = 0x21,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -45,6 +45,7 @@ typedef struct SiFiveEState {
|
|||
|
||||
/*< public >*/
|
||||
SiFiveESoCState soc;
|
||||
bool revb;
|
||||
} SiFiveEState;
|
||||
|
||||
#define TYPE_RISCV_E_MACHINE MACHINE_TYPE_NAME("sifive_e")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* sifive System-on-Chip general purpose input/output register definition
|
||||
* SiFive System-on-Chip general purpose input/output register definition
|
||||
*
|
||||
* Copyright 2019 AdaCore
|
||||
*
|
||||
|
@ -10,10 +10,12 @@
|
|||
* This code is licensed under the GPL version 2 or later. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef SIFIVE_GPIO_H
|
||||
#define SIFIVE_GPIO_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_SIFIVE_GPIO "sifive_soc.gpio"
|
||||
#define SIFIVE_GPIO(obj) OBJECT_CHECK(SIFIVEGPIOState, (obj), TYPE_SIFIVE_GPIO)
|
||||
|
||||
|
@ -67,6 +69,8 @@ typedef struct SIFIVEGPIOState {
|
|||
uint32_t in;
|
||||
uint32_t in_mask;
|
||||
|
||||
/* config */
|
||||
uint32_t ngpio;
|
||||
} SIFIVEGPIOState;
|
||||
|
||||
#endif
|
||||
#endif /* SIFIVE_GPIO_H */
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "hw/net/cadence_gem.h"
|
||||
#include "hw/riscv/riscv_hart.h"
|
||||
#include "hw/riscv/sifive_cpu.h"
|
||||
#include "hw/riscv/sifive_gpio.h"
|
||||
#include "hw/riscv/sifive_u_prci.h"
|
||||
#include "hw/riscv/sifive_u_otp.h"
|
||||
|
||||
|
@ -40,6 +41,7 @@ typedef struct SiFiveUSoCState {
|
|||
RISCVHartArrayState u_cpus;
|
||||
DeviceState *plic;
|
||||
SiFiveUPRCIState prci;
|
||||
SIFIVEGPIOState gpio;
|
||||
SiFiveUOTPState otp;
|
||||
CadenceGEMState gem;
|
||||
|
||||
|
@ -61,6 +63,7 @@ typedef struct SiFiveUState {
|
|||
int fdt_size;
|
||||
|
||||
bool start_in_flash;
|
||||
uint32_t msel;
|
||||
uint32_t serial;
|
||||
} SiFiveUState;
|
||||
|
||||
|
@ -73,7 +76,9 @@ enum {
|
|||
SIFIVE_U_PRCI,
|
||||
SIFIVE_U_UART0,
|
||||
SIFIVE_U_UART1,
|
||||
SIFIVE_U_GPIO,
|
||||
SIFIVE_U_OTP,
|
||||
SIFIVE_U_DMC,
|
||||
SIFIVE_U_FLASH0,
|
||||
SIFIVE_U_DRAM,
|
||||
SIFIVE_U_GEM,
|
||||
|
@ -83,6 +88,22 @@ enum {
|
|||
enum {
|
||||
SIFIVE_U_UART0_IRQ = 4,
|
||||
SIFIVE_U_UART1_IRQ = 5,
|
||||
SIFIVE_U_GPIO_IRQ0 = 7,
|
||||
SIFIVE_U_GPIO_IRQ1 = 8,
|
||||
SIFIVE_U_GPIO_IRQ2 = 9,
|
||||
SIFIVE_U_GPIO_IRQ3 = 10,
|
||||
SIFIVE_U_GPIO_IRQ4 = 11,
|
||||
SIFIVE_U_GPIO_IRQ5 = 12,
|
||||
SIFIVE_U_GPIO_IRQ6 = 13,
|
||||
SIFIVE_U_GPIO_IRQ7 = 14,
|
||||
SIFIVE_U_GPIO_IRQ8 = 15,
|
||||
SIFIVE_U_GPIO_IRQ9 = 16,
|
||||
SIFIVE_U_GPIO_IRQ10 = 17,
|
||||
SIFIVE_U_GPIO_IRQ11 = 18,
|
||||
SIFIVE_U_GPIO_IRQ12 = 19,
|
||||
SIFIVE_U_GPIO_IRQ13 = 20,
|
||||
SIFIVE_U_GPIO_IRQ14 = 21,
|
||||
SIFIVE_U_GPIO_IRQ15 = 22,
|
||||
SIFIVE_U_GEM_IRQ = 0x35
|
||||
};
|
||||
|
||||
|
@ -91,6 +112,12 @@ enum {
|
|||
SIFIVE_U_RTCCLK_FREQ = 1000000
|
||||
};
|
||||
|
||||
enum {
|
||||
MSEL_MEMMAP_QSPI0_FLASH = 1,
|
||||
MSEL_L2LIM_QSPI0_FLASH = 6,
|
||||
MSEL_L2LIM_QSPI2_SD = 11
|
||||
};
|
||||
|
||||
#define SIFIVE_U_MANAGEMENT_CPU_COUNT 1
|
||||
#define SIFIVE_U_COMPUTE_CPU_COUNT 4
|
||||
|
||||
|
|
|
@ -126,9 +126,7 @@ static void riscv_any_cpu_init(Object *obj)
|
|||
set_resetvec(env, DEFAULT_RSTVEC);
|
||||
}
|
||||
|
||||
#if defined(TARGET_RISCV32)
|
||||
|
||||
static void riscv_base32_cpu_init(Object *obj)
|
||||
static void riscv_base_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
/* We set this in the realise function */
|
||||
|
@ -136,15 +134,26 @@ static void riscv_base32_cpu_init(Object *obj)
|
|||
set_resetvec(env, DEFAULT_RSTVEC);
|
||||
}
|
||||
|
||||
static void rv32gcsu_priv1_10_0_cpu_init(Object *obj)
|
||||
static void rvxx_sifive_u_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
||||
set_misa(env, RVXLEN | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
||||
set_priv_version(env, PRIV_VERSION_1_10_0);
|
||||
set_resetvec(env, DEFAULT_RSTVEC);
|
||||
set_resetvec(env, 0x1004);
|
||||
}
|
||||
|
||||
static void rv32imcu_nommu_cpu_init(Object *obj)
|
||||
static void rvxx_sifive_e_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
set_misa(env, RVXLEN | RVI | RVM | RVA | RVC | RVU);
|
||||
set_priv_version(env, PRIV_VERSION_1_10_0);
|
||||
set_resetvec(env, 0x1004);
|
||||
qdev_prop_set_bit(DEVICE(obj), "mmu", false);
|
||||
}
|
||||
|
||||
#if defined(TARGET_RISCV32)
|
||||
|
||||
static void rv32_ibex_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
set_misa(env, RV32 | RVI | RVM | RVC | RVU);
|
||||
|
@ -153,16 +162,7 @@ static void rv32imcu_nommu_cpu_init(Object *obj)
|
|||
qdev_prop_set_bit(DEVICE(obj), "mmu", false);
|
||||
}
|
||||
|
||||
static void rv32imacu_nommu_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU);
|
||||
set_priv_version(env, PRIV_VERSION_1_10_0);
|
||||
set_resetvec(env, DEFAULT_RSTVEC);
|
||||
qdev_prop_set_bit(DEVICE(obj), "mmu", false);
|
||||
}
|
||||
|
||||
static void rv32imafcu_nommu_cpu_init(Object *obj)
|
||||
static void rv32_imafcu_nommu_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVC | RVU);
|
||||
|
@ -171,33 +171,6 @@ static void rv32imafcu_nommu_cpu_init(Object *obj)
|
|||
qdev_prop_set_bit(DEVICE(obj), "mmu", false);
|
||||
}
|
||||
|
||||
#elif defined(TARGET_RISCV64)
|
||||
|
||||
static void riscv_base64_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
/* We set this in the realise function */
|
||||
set_misa(env, 0);
|
||||
set_resetvec(env, DEFAULT_RSTVEC);
|
||||
}
|
||||
|
||||
static void rv64gcsu_priv1_10_0_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
|
||||
set_priv_version(env, PRIV_VERSION_1_10_0);
|
||||
set_resetvec(env, DEFAULT_RSTVEC);
|
||||
}
|
||||
|
||||
static void rv64imacu_nommu_cpu_init(Object *obj)
|
||||
{
|
||||
CPURISCVState *env = &RISCV_CPU(obj)->env;
|
||||
set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU);
|
||||
set_priv_version(env, PRIV_VERSION_1_10_0);
|
||||
set_resetvec(env, DEFAULT_RSTVEC);
|
||||
qdev_prop_set_bit(DEVICE(obj), "mmu", false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model)
|
||||
|
@ -603,15 +576,15 @@ static const TypeInfo riscv_cpu_type_infos[] = {
|
|||
},
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_ANY, riscv_any_cpu_init),
|
||||
#if defined(TARGET_RISCV32)
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_BASE32, riscv_base32_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_IBEX, rv32imcu_nommu_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E31, rv32imacu_nommu_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E34, rv32imafcu_nommu_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U34, rv32gcsu_priv1_10_0_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_BASE32, riscv_base_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_IBEX, rv32_ibex_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E31, rvxx_sifive_e_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E34, rv32_imafcu_nommu_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U34, rvxx_sifive_u_cpu_init),
|
||||
#elif defined(TARGET_RISCV64)
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_BASE64, riscv_base64_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E51, rv64imacu_nommu_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U54, rv64gcsu_priv1_10_0_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_BASE64, riscv_base_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E51, rvxx_sifive_e_cpu_init),
|
||||
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U54, rvxx_sifive_u_cpu_init),
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -435,8 +435,13 @@ restart:
|
|||
hwaddr vbase;
|
||||
|
||||
/* Do the second stage translation on the base PTE address. */
|
||||
get_physical_address(env, &vbase, &vbase_prot, base, access_type,
|
||||
mmu_idx, false, true);
|
||||
int vbase_ret = get_physical_address(env, &vbase, &vbase_prot,
|
||||
base, MMU_DATA_LOAD,
|
||||
mmu_idx, false, true);
|
||||
|
||||
if (vbase_ret != TRANSLATE_SUCCESS) {
|
||||
return vbase_ret;
|
||||
}
|
||||
|
||||
pte_addr = vbase + idx * ptesize;
|
||||
} else {
|
||||
|
|
|
@ -76,3 +76,8 @@ DEF_HELPER_2(mret, tl, env, tl)
|
|||
DEF_HELPER_1(wfi, void, env)
|
||||
DEF_HELPER_1(tlb_flush, void, env)
|
||||
#endif
|
||||
|
||||
/* Hypervisor functions */
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
DEF_HELPER_1(hyp_tlb_flush, void, env)
|
||||
#endif
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
@r2 ....... ..... ..... ... ..... ....... %rs1 %rd
|
||||
|
||||
@hfence_gvma ....... ..... ..... ... ..... ....... %rs2 %rs1
|
||||
@hfence_bvma ....... ..... ..... ... ..... ....... %rs2 %rs1
|
||||
@hfence_vvma ....... ..... ..... ... ..... ....... %rs2 %rs1
|
||||
|
||||
@sfence_vma ....... ..... ..... ... ..... ....... %rs2 %rs1
|
||||
@sfence_vm ....... ..... ..... ... ..... ....... %rs1
|
||||
|
@ -77,8 +77,6 @@ uret 0000000 00010 00000 000 00000 1110011
|
|||
sret 0001000 00010 00000 000 00000 1110011
|
||||
mret 0011000 00010 00000 000 00000 1110011
|
||||
wfi 0001000 00101 00000 000 00000 1110011
|
||||
hfence_gvma 0110001 ..... ..... 000 00000 1110011 @hfence_gvma
|
||||
hfence_bvma 0010001 ..... ..... 000 00000 1110011 @hfence_bvma
|
||||
sfence_vma 0001001 ..... ..... 000 00000 1110011 @sfence_vma
|
||||
sfence_vm 0001000 00100 ..... 000 00000 1110011 @sfence_vm
|
||||
|
||||
|
@ -207,3 +205,7 @@ fcvt_w_d 1100001 00000 ..... ... ..... 1010011 @r2_rm
|
|||
fcvt_wu_d 1100001 00001 ..... ... ..... 1010011 @r2_rm
|
||||
fcvt_d_w 1101001 00000 ..... ... ..... 1010011 @r2_rm
|
||||
fcvt_d_wu 1101001 00001 ..... ... ..... 1010011 @r2_rm
|
||||
|
||||
# *** RV32H Base Instruction Set ***
|
||||
hfence_gvma 0110001 ..... ..... 000 00000 1110011 @hfence_gvma
|
||||
hfence_vvma 0010001 ..... ..... 000 00000 1110011 @hfence_vvma
|
||||
|
|
|
@ -95,41 +95,3 @@ static bool trans_sfence_vm(DisasContext *ctx, arg_sfence_vm *a)
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool trans_hfence_gvma(DisasContext *ctx, arg_sfence_vma *a)
|
||||
{
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (has_ext(ctx, RVH)) {
|
||||
/* Hpervisor extensions exist */
|
||||
/*
|
||||
* if (env->priv == PRV_M ||
|
||||
* (env->priv == PRV_S &&
|
||||
* !riscv_cpu_virt_enabled(env) &&
|
||||
* get_field(ctx->mstatus_fs, MSTATUS_TVM))) {
|
||||
*/
|
||||
gen_helper_tlb_flush(cpu_env);
|
||||
return true;
|
||||
/* } */
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool trans_hfence_bvma(DisasContext *ctx, arg_sfence_vma *a)
|
||||
{
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (has_ext(ctx, RVH)) {
|
||||
/* Hpervisor extensions exist */
|
||||
/*
|
||||
* if (env->priv == PRV_M ||
|
||||
* (env->priv == PRV_S &&
|
||||
* !riscv_cpu_virt_enabled(env) &&
|
||||
* get_field(ctx->mstatus_fs, MSTATUS_TVM))) {
|
||||
*/
|
||||
gen_helper_tlb_flush(cpu_env);
|
||||
return true;
|
||||
/* } */
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,20 @@
|
|||
return false; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* RISC-V requires NaN-boxing of narrower width floating
|
||||
* point values. This applies when a 32-bit value is
|
||||
* assigned to a 64-bit FP register. Thus this does not
|
||||
* apply when the RVD extension is not present.
|
||||
*/
|
||||
static void gen_nanbox_fpr(DisasContext *ctx, int regno)
|
||||
{
|
||||
if (has_ext(ctx, RVD)) {
|
||||
tcg_gen_ori_i64(cpu_fpr[regno], cpu_fpr[regno],
|
||||
MAKE_64BIT_MASK(32, 32));
|
||||
}
|
||||
}
|
||||
|
||||
static bool trans_flw(DisasContext *ctx, arg_flw *a)
|
||||
{
|
||||
TCGv t0 = tcg_temp_new();
|
||||
|
@ -32,8 +46,7 @@ static bool trans_flw(DisasContext *ctx, arg_flw *a)
|
|||
tcg_gen_addi_tl(t0, t0, a->imm);
|
||||
|
||||
tcg_gen_qemu_ld_i64(cpu_fpr[a->rd], t0, ctx->mem_idx, MO_TEUL);
|
||||
/* RISC-V requires NaN-boxing of narrower width floating point values */
|
||||
tcg_gen_ori_i64(cpu_fpr[a->rd], cpu_fpr[a->rd], 0xffffffff00000000ULL);
|
||||
gen_nanbox_fpr(ctx, a->rd);
|
||||
|
||||
tcg_temp_free(t0);
|
||||
mark_fs_dirty(ctx);
|
||||
|
|
37
target/riscv/insn_trans/trans_rvh.inc.c
Normal file
37
target/riscv/insn_trans/trans_rvh.inc.c
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* RISC-V translation routines for the RVXI Base Integer Instruction Set.
|
||||
*
|
||||
* Copyright (c) 2020 Western Digital
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
static bool trans_hfence_gvma(DisasContext *ctx, arg_sfence_vma *a)
|
||||
{
|
||||
REQUIRE_EXT(ctx, RVH);
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
gen_helper_hyp_tlb_flush(cpu_env);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool trans_hfence_vvma(DisasContext *ctx, arg_sfence_vma *a)
|
||||
{
|
||||
REQUIRE_EXT(ctx, RVH);
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
gen_helper_hyp_tlb_flush(cpu_env);
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
|
@ -194,4 +194,17 @@ void helper_tlb_flush(CPURISCVState *env)
|
|||
}
|
||||
}
|
||||
|
||||
void helper_hyp_tlb_flush(CPURISCVState *env)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
|
||||
if (env->priv == PRV_M ||
|
||||
(env->priv == PRV_S && !riscv_cpu_virt_enabled(env))) {
|
||||
tlb_flush(cs);
|
||||
return;
|
||||
}
|
||||
|
||||
riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
|
|
|
@ -233,12 +233,16 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* if size is unknown (0), assume that all bytes
|
||||
* from addr to the end of the page will be accessed.
|
||||
*/
|
||||
if (size == 0) {
|
||||
pmp_size = -(addr | TARGET_PAGE_MASK);
|
||||
if (riscv_feature(env, RISCV_FEATURE_MMU)) {
|
||||
/*
|
||||
* If size is unknown (0), assume that all bytes
|
||||
* from addr to the end of the page will be accessed.
|
||||
*/
|
||||
pmp_size = -(addr | TARGET_PAGE_MASK);
|
||||
} else {
|
||||
pmp_size = sizeof(target_ulong);
|
||||
}
|
||||
} else {
|
||||
pmp_size = size;
|
||||
}
|
||||
|
|
|
@ -711,6 +711,7 @@ static bool gen_shift(DisasContext *ctx, arg_r *a,
|
|||
#include "insn_trans/trans_rva.inc.c"
|
||||
#include "insn_trans/trans_rvf.inc.c"
|
||||
#include "insn_trans/trans_rvd.inc.c"
|
||||
#include "insn_trans/trans_rvh.inc.c"
|
||||
#include "insn_trans/trans_privileged.inc.c"
|
||||
|
||||
/* Include the auto-generated decoder for 16 bit insn */
|
||||
|
|
Loading…
Reference in a new issue