mirror of https://github.com/xemu-project/xemu.git
* Refactor M-profile systick to use Clocks instead of system_clock_scale global
* clock: Provide builtin multiplier/divider * Add A64FX processor model * Enable MVE emulation in Cortex-M55 * hw: Add compat machines for 6.2 * hw/intc/arm_gicv3: Replace mis-used MEMTX_* constants by booleans * hw/arm/raspi: Remove deprecated raspi2/raspi3 aliases -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmEvV40ZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3gO9EACbRq2XN67/3n8icVlCA/R0 c1NKdar5yngG7EUoKg8rIHWSG4nlvGQxEsInjqccWpXUBwhRbmjIoI7OWOTVcNKL IxZPDgS619CPpUrOPtOouYYl3RiKpNS6v1BAne1IsZrMtydrioRCZIh8Wiz1cAod Ok8ts1hZIyg+n5gpYjeXs57Afsms4SCnt0zJT/4/VZXZmi+ohHVOzFX/ZGKmZyMP 2lUHX8DcBCPW6JPt4poIP9MVs+44v9RajSMHfHFkNkJb0Q8sh+fgKbAdnxOW/6d+ B381tMPtT5D1FZ5UmIwJl6XzY3JLJyU2ySQOtXlnkGWniuihxIwG0qBxwMsbPFrl bcTuJK5C20DmjFNH9jk9LMzKD4rDrLcADfoo/AfPe/JqTpE1t6zXcOzAgVTrPNx/ tSDAXHAsmnJ2Y5O7h5MRUSG6R4h7+PRoutXQyGRi39VMNkiPmqltQtM9N1UaguOe +X3w3yjtsCotN1cLWq0u/UuC7FoDoCge/uBSzYIKoeu/WyJFGjuocWGg7TmwOJyd TzqrSOqCsAx9DhVJ1trO82qb31zpyFf4B1C05PMaoudzlgMfWfvXVDrtcRmO+kjn DmUB4vqz5g2jGroJWAVuN+wz245KqkSaHA6RRI5wf9n57H56T0Rfxa2o73A9TLZy M4T9XiJxDByGODYTu8ejXQ== =D5pQ -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20210901' into staging * Refactor M-profile systick to use Clocks instead of system_clock_scale global * clock: Provide builtin multiplier/divider * Add A64FX processor model * Enable MVE emulation in Cortex-M55 * hw: Add compat machines for 6.2 * hw/intc/arm_gicv3: Replace mis-used MEMTX_* constants by booleans * hw/arm/raspi: Remove deprecated raspi2/raspi3 aliases # gpg: Signature made Wed 01 Sep 2021 11:35:57 BST # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20210901: (51 commits) arm: Remove system_clock_scale global hw/timer/stellaris-gptm: Use Clock input instead of system_clock_scale hw/arm/stellaris: Split stellaris-gptm into its own file hw/arm/stellaris: Fix code style issues in GPTM code hw/timer/armv7m_systick: Use clock inputs instead of system_clock_scale hw/arm/msf2-soc: Wire up refclk hw/arm/msf2: Use Clock input to MSF2_SOC instead of m3clk property hw/arm/msf2_soc: Don't allocate separate MemoryRegions hw/arm/stellaris: Wire sysclk up to armv7m hw/arm/stellaris: split stellaris_sys_init() hw/arm/nrf51: Wire up sysclk hw/arm/stm32vldiscovery: Delete trailing blank line hw/arm/stm32f405: Wire up sysclk and refclk hw/arm/stm32f205: Wire up sysclk and refclk hw/arm/stm32f100: Wire up sysclk and refclk hw/arm: Don't allocate separate MemoryRegions in stm32 SoC realize clock: Provide builtin multiplier/divider hw/arm/mps2.c: Connect up armv7m clocks armsse: Wire up systick cpuclk clock hw/arm/armv7m: Create input clocks ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
079b1252e9
|
@ -617,6 +617,7 @@ F: hw/intc/gic_internal.h
|
|||
F: hw/misc/a9scu.c
|
||||
F: hw/misc/arm11scu.c
|
||||
F: hw/misc/arm_l2x0.c
|
||||
F: hw/misc/armv7m_ras.c
|
||||
F: hw/timer/a9gtimer*
|
||||
F: hw/timer/arm*
|
||||
F: include/hw/arm/arm*.h
|
||||
|
@ -626,6 +627,7 @@ F: include/hw/misc/arm11scu.h
|
|||
F: include/hw/timer/a9gtimer.h
|
||||
F: include/hw/timer/arm_mptimer.h
|
||||
F: include/hw/timer/armv7m_systick.h
|
||||
F: include/hw/misc/armv7m_ras.h
|
||||
F: tests/qtest/test-arm-mptimer.c
|
||||
|
||||
Exynos
|
||||
|
|
|
@ -207,13 +207,6 @@ this CPU is also deprecated.
|
|||
System emulator machines
|
||||
------------------------
|
||||
|
||||
Raspberry Pi ``raspi2`` and ``raspi3`` machines (since 5.2)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The Raspberry Pi machines come in various models (A, A+, B, B+). To be able
|
||||
to distinguish which model QEMU is implementing, the ``raspi2`` and ``raspi3``
|
||||
machines have been renamed ``raspi2b`` and ``raspi3b``.
|
||||
|
||||
Aspeed ``swift-bmc`` machine (since 6.1)
|
||||
''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
|
|
|
@ -574,6 +574,13 @@ This machine has been renamed ``fuloong2e``.
|
|||
These machine types were very old and likely could not be used for live
|
||||
migration from old QEMU versions anymore. Use a newer machine type instead.
|
||||
|
||||
Raspberry Pi ``raspi2`` and ``raspi3`` machines (removed in 6.2)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The Raspberry Pi machines come in various models (A, A+, B, B+). To be able
|
||||
to distinguish which model QEMU is implementing, the ``raspi2`` and ``raspi3``
|
||||
machines have been renamed ``raspi2b`` and ``raspi3b``.
|
||||
|
||||
|
||||
linux-user mode CPUs
|
||||
--------------------
|
||||
|
|
|
@ -260,6 +260,29 @@ clocks get the new clock period value: *Clock 2*, *Clock 3* and *Clock 4*.
|
|||
It is not possible to disconnect a clock or to change the clock connection
|
||||
after it is connected.
|
||||
|
||||
Clock multiplier and divider settings
|
||||
-------------------------------------
|
||||
|
||||
By default, when clocks are connected together, the child
|
||||
clocks run with the same period as their source (parent) clock.
|
||||
The Clock API supports a built-in period multiplier/divider
|
||||
mechanism so you can configure a clock to make its children
|
||||
run at a different period from its own. If you call the
|
||||
``clock_set_mul_div()`` function you can specify the clock's
|
||||
multiplier and divider values. The children of that clock
|
||||
will all run with a period of ``parent_period * multiplier / divider``.
|
||||
For instance, if the clock has a frequency of 8MHz and you set its
|
||||
multiplier to 2 and its divider to 3, the child clocks will run
|
||||
at 12MHz.
|
||||
|
||||
You can change the multiplier and divider of a clock at runtime,
|
||||
so you can use this to model clock controller devices which
|
||||
have guest-programmable frequency multipliers or dividers.
|
||||
|
||||
Note that ``clock_set_mul_div()`` does not automatically call
|
||||
``clock_propagate()``. If you make a runtime change to the
|
||||
multiplier or divider you must call clock_propagate() yourself.
|
||||
|
||||
Unconnected input clocks
|
||||
------------------------
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ Nodes
|
|||
|
||||
A node can be of four types:
|
||||
|
||||
- **QNODE_MACHINE**: for example ``arm/raspi2``
|
||||
- **QNODE_MACHINE**: for example ``arm/raspi2b``
|
||||
- **QNODE_DRIVER**: for example ``generic-sdhci``
|
||||
- **QNODE_INTERFACE**: for example ``sdhci`` (interface for all ``-sdhci``
|
||||
drivers).
|
||||
|
@ -119,12 +119,12 @@ It is possible to troubleshoot unavailable tests by running::
|
|||
# |-> dest='i440FX-pcihost' type=0 (node=0x5591421117f0)
|
||||
# src=''
|
||||
# |-> dest='x86_64/pc' type=0 (node=0x559142111600)
|
||||
# |-> dest='arm/raspi2' type=0 (node=0x559142110740)
|
||||
# |-> dest='arm/raspi2b' type=0 (node=0x559142110740)
|
||||
...
|
||||
# }
|
||||
# ALL QGRAPH NODES: {
|
||||
# name='virtio-net-tests/announce-self' type=3 cmd_line='(null)' [available]
|
||||
# name='arm/raspi2' type=0 cmd_line='-M raspi2 ' [UNAVAILABLE]
|
||||
# name='arm/raspi2b' type=0 cmd_line='-M raspi2b ' [UNAVAILABLE]
|
||||
...
|
||||
# }
|
||||
|
||||
|
@ -135,8 +135,8 @@ qgraph path in the "ALL QGRAPH EDGES" output as follows: '' -> 'x86_64/pc' ->
|
|||
'virtio-net'. The root of the qgraph is '' and the depth first search begins
|
||||
there.
|
||||
|
||||
The ``arm/raspi`` machine node is listed as "UNAVAILABLE". Although it is
|
||||
reachable from the root via '' -> 'arm/raspi2' the node is unavailable because
|
||||
The ``arm/raspi2b`` machine node is listed as "UNAVAILABLE". Although it is
|
||||
reachable from the root via '' -> 'arm/raspi2b' the node is unavailable because
|
||||
the QEMU binary did not list it when queried by the framework. This is expected
|
||||
because we used the ``qemu-system-x86_64`` binary which does not support ARM
|
||||
machine types.
|
||||
|
@ -158,7 +158,7 @@ Here we continue the ``sdhci`` use case, with the following scenario:
|
|||
- ``sdhci-test`` aims to test the ``read[q,w], writeq`` functions
|
||||
offered by the ``sdhci`` drivers.
|
||||
- The current ``sdhci`` device is supported by both ``x86_64/pc`` and ``ARM``
|
||||
(in this example we focus on the ``arm-raspi2``) machines.
|
||||
(in this example we focus on the ``arm-raspi2b``) machines.
|
||||
- QEMU offers 2 types of drivers: ``QSDHCI_MemoryMapped`` for ``ARM`` and
|
||||
``QSDHCI_PCI`` for ``x86_64/pc``. Both implement the
|
||||
``read[q,w], writeq`` functions.
|
||||
|
@ -180,11 +180,11 @@ In order to implement such scenario in qgraph, the test developer needs to:
|
|||
all the pci drivers available)
|
||||
|
||||
``sdhci-pci --consumes--> pci-bus``
|
||||
- Create an ``arm/raspi2`` machine node. This machine ``contains``
|
||||
- Create an ``arm/raspi2b`` machine node. This machine ``contains``
|
||||
a ``generic-sdhci`` memory mapped ``sdhci`` driver node, representing
|
||||
``QSDHCI_MemoryMapped``.
|
||||
|
||||
``arm/raspi2 --contains--> generic-sdhci``
|
||||
``arm/raspi2b --contains--> generic-sdhci``
|
||||
- Create the ``sdhci`` interface node. This interface offers the
|
||||
functions that are shared by all ``sdhci`` devices.
|
||||
The interface is produced by ``sdhci-pci`` and ``generic-sdhci``,
|
||||
|
@ -199,7 +199,7 @@ In order to implement such scenario in qgraph, the test developer needs to:
|
|||
|
||||
``sdhci-test --consumes--> sdhci``
|
||||
|
||||
``arm-raspi2`` machine, simplified from
|
||||
``arm-raspi2b`` machine, simplified from
|
||||
``tests/qtest/libqos/arm-raspi2-machine.c``::
|
||||
|
||||
#include "qgraph.h"
|
||||
|
@ -217,7 +217,7 @@ In order to implement such scenario in qgraph, the test developer needs to:
|
|||
return &machine->alloc;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in arm/raspi2\n", interface);
|
||||
fprintf(stderr, "%s not present in arm/raspi2b\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
|
@ -229,7 +229,7 @@ In order to implement such scenario in qgraph, the test developer needs to:
|
|||
return &machine->sdhci.obj;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in arm/raspi2\n", device);
|
||||
fprintf(stderr, "%s not present in arm/raspi2b\n", device);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
|
@ -253,10 +253,10 @@ In order to implement such scenario in qgraph, the test developer needs to:
|
|||
|
||||
static void raspi2_register_nodes(void)
|
||||
{
|
||||
/* arm/raspi2 --contains--> generic-sdhci */
|
||||
qos_node_create_machine("arm/raspi2",
|
||||
/* arm/raspi2b --contains--> generic-sdhci */
|
||||
qos_node_create_machine("arm/raspi2b",
|
||||
qos_create_machine_arm_raspi2);
|
||||
qos_node_contains("arm/raspi2", "generic-sdhci", NULL);
|
||||
qos_node_contains("arm/raspi2b", "generic-sdhci", NULL);
|
||||
}
|
||||
|
||||
libqos_init(raspi2_register_nodes);
|
||||
|
@ -470,7 +470,7 @@ In the above example, all possible types of relations are created::
|
|||
|
|
||||
+--produces-- +
|
||||
|
|
||||
arm/raspi2 --contains--> generic-sdhci
|
||||
arm/raspi2b --contains--> generic-sdhci
|
||||
|
||||
or inverting the consumes edge in consumed_by::
|
||||
|
||||
|
@ -486,7 +486,7 @@ or inverting the consumes edge in consumed_by::
|
|||
|
|
||||
+--produces-- +
|
||||
|
|
||||
arm/raspi2 --contains--> generic-sdhci
|
||||
arm/raspi2b --contains--> generic-sdhci
|
||||
|
||||
Adding a new test
|
||||
"""""""""""""""""
|
||||
|
@ -536,7 +536,7 @@ Final graph will be like this::
|
|||
|
|
||||
+--produces-- +
|
||||
|
|
||||
arm/raspi2 --contains--> generic-sdhci
|
||||
arm/raspi2b --contains--> generic-sdhci
|
||||
|
||||
or inverting the consumes edge in consumed_by::
|
||||
|
||||
|
@ -552,7 +552,7 @@ or inverting the consumes edge in consumed_by::
|
|||
|
|
||||
+--produces-- +
|
||||
|
|
||||
arm/raspi2 --contains--> generic-sdhci
|
||||
arm/raspi2b --contains--> generic-sdhci
|
||||
|
||||
Assuming there the binary is
|
||||
``QTEST_QEMU_BINARY=./qemu-system-x86_64``
|
||||
|
@ -561,7 +561,7 @@ a valid test path will be:
|
|||
|
||||
and for the binary ``QTEST_QEMU_BINARY=./qemu-system-arm``:
|
||||
|
||||
``/arm/raspi2/generic-sdhci/sdhci/sdhci-test``
|
||||
``/arm/raspi2b/generic-sdhci/sdhci/sdhci-test``
|
||||
|
||||
Additional examples are also in ``test-qgraph.c``
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ Supported guest CPU types:
|
|||
- ``cortex-a53`` (64-bit)
|
||||
- ``cortex-a57`` (64-bit)
|
||||
- ``cortex-a72`` (64-bit)
|
||||
- ``a64fx`` (64-bit)
|
||||
- ``host`` (with KVM only)
|
||||
- ``max`` (same as ``host`` for KVM; best possible emulation with TCG)
|
||||
|
||||
|
|
|
@ -198,7 +198,6 @@ static void parts128_default_nan(FloatParts128 *p, float_status *status)
|
|||
static uint64_t parts_silence_nan_frac(uint64_t frac, float_status *status)
|
||||
{
|
||||
g_assert(!no_signaling_nans(status));
|
||||
g_assert(!status->default_nan_mode);
|
||||
|
||||
/* The only snan_bit_is_one target without default_nan_mode is HPPA. */
|
||||
if (snan_bit_is_one(status)) {
|
||||
|
|
|
@ -235,6 +235,7 @@ config STELLARIS
|
|||
select SSI_SD
|
||||
select STELLARIS_INPUT
|
||||
select STELLARIS_ENET # ethernet
|
||||
select STELLARIS_GPTM # general purpose timer module
|
||||
select UNIMP
|
||||
|
||||
config STM32VLDISCOVERY
|
||||
|
|
|
@ -689,17 +689,6 @@ static void armsse_forward_sec_resp_cfg(ARMSSE *s)
|
|||
qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in);
|
||||
}
|
||||
|
||||
static void armsse_mainclk_update(void *opaque, ClockEvent event)
|
||||
{
|
||||
ARMSSE *s = ARM_SSE(opaque);
|
||||
|
||||
/*
|
||||
* Set system_clock_scale from our Clock input; this is what
|
||||
* controls the tick rate of the CPU SysTick timer.
|
||||
*/
|
||||
system_clock_scale = clock_ticks_to_ns(s->mainclk, 1);
|
||||
}
|
||||
|
||||
static void armsse_init(Object *obj)
|
||||
{
|
||||
ARMSSE *s = ARM_SSE(obj);
|
||||
|
@ -711,8 +700,7 @@ static void armsse_init(Object *obj)
|
|||
assert(info->sram_banks <= MAX_SRAM_BANKS);
|
||||
assert(info->num_cpus <= SSE_MAX_CPUS);
|
||||
|
||||
s->mainclk = qdev_init_clock_in(DEVICE(s), "MAINCLK",
|
||||
armsse_mainclk_update, s, ClockUpdate);
|
||||
s->mainclk = qdev_init_clock_in(DEVICE(s), "MAINCLK", NULL, NULL, 0);
|
||||
s->s32kclk = qdev_init_clock_in(DEVICE(s), "S32KCLK", NULL, NULL, 0);
|
||||
|
||||
memory_region_init(&s->container, obj, "armsse-container", UINT64_MAX);
|
||||
|
@ -995,6 +983,9 @@ static void armsse_realize(DeviceState *dev, Error **errp)
|
|||
int j;
|
||||
char *gpioname;
|
||||
|
||||
qdev_connect_clock_in(cpudev, "cpuclk", s->mainclk);
|
||||
/* The SSE subsystems do not wire up a systick refclk */
|
||||
|
||||
qdev_prop_set_uint32(cpudev, "num-irq", s->exp_numirq + NUM_SSE_IRQS);
|
||||
/*
|
||||
* In real hardware the initial Secure VTOR is set from the INITSVTOR*
|
||||
|
@ -1651,9 +1642,6 @@ static void armsse_realize(DeviceState *dev, Error **errp)
|
|||
* devices in the ARMSSE.
|
||||
*/
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container);
|
||||
|
||||
/* Set initial system_clock_scale from MAINCLK */
|
||||
armsse_mainclk_update(s, ClockUpdate);
|
||||
}
|
||||
|
||||
static void armsse_idau_check(IDAUInterface *ii, uint32_t address,
|
||||
|
|
260
hw/arm/armv7m.c
260
hw/arm/armv7m.c
|
@ -14,11 +14,14 @@
|
|||
#include "hw/arm/boot.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "elf.h"
|
||||
#include "sysemu/reset.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/log.h"
|
||||
#include "target/arm/idau.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
/* Bitbanded IO. Each word corresponds to a single bit. */
|
||||
|
||||
|
@ -124,6 +127,122 @@ static const hwaddr bitband_output_addr[ARMV7M_NUM_BITBANDS] = {
|
|||
0x22000000, 0x42000000
|
||||
};
|
||||
|
||||
static MemTxResult v7m_sysreg_ns_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
MemoryRegion *mr = opaque;
|
||||
|
||||
if (attrs.secure) {
|
||||
/* S accesses to the alias act like NS accesses to the real region */
|
||||
attrs.secure = 0;
|
||||
return memory_region_dispatch_write(mr, addr, value,
|
||||
size_memop(size) | MO_TE, attrs);
|
||||
} else {
|
||||
/* NS attrs are RAZ/WI for privileged, and BusFault for user */
|
||||
if (attrs.user) {
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
return MEMTX_OK;
|
||||
}
|
||||
}
|
||||
|
||||
static MemTxResult v7m_sysreg_ns_read(void *opaque, hwaddr addr,
|
||||
uint64_t *data, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
MemoryRegion *mr = opaque;
|
||||
|
||||
if (attrs.secure) {
|
||||
/* S accesses to the alias act like NS accesses to the real region */
|
||||
attrs.secure = 0;
|
||||
return memory_region_dispatch_read(mr, addr, data,
|
||||
size_memop(size) | MO_TE, attrs);
|
||||
} else {
|
||||
/* NS attrs are RAZ/WI for privileged, and BusFault for user */
|
||||
if (attrs.user) {
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
*data = 0;
|
||||
return MEMTX_OK;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps v7m_sysreg_ns_ops = {
|
||||
.read_with_attrs = v7m_sysreg_ns_read,
|
||||
.write_with_attrs = v7m_sysreg_ns_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static MemTxResult v7m_systick_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
ARMv7MState *s = opaque;
|
||||
MemoryRegion *mr;
|
||||
|
||||
/* Direct the access to the correct systick */
|
||||
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systick[attrs.secure]), 0);
|
||||
return memory_region_dispatch_write(mr, addr, value,
|
||||
size_memop(size) | MO_TE, attrs);
|
||||
}
|
||||
|
||||
static MemTxResult v7m_systick_read(void *opaque, hwaddr addr,
|
||||
uint64_t *data, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
ARMv7MState *s = opaque;
|
||||
MemoryRegion *mr;
|
||||
|
||||
/* Direct the access to the correct systick */
|
||||
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systick[attrs.secure]), 0);
|
||||
return memory_region_dispatch_read(mr, addr, data, size_memop(size) | MO_TE,
|
||||
attrs);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps v7m_systick_ops = {
|
||||
.read_with_attrs = v7m_systick_read,
|
||||
.write_with_attrs = v7m_systick_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
/*
|
||||
* Unassigned portions of the PPB space are RAZ/WI for privileged
|
||||
* accesses, and fault for non-privileged accesses.
|
||||
*/
|
||||
static MemTxResult ppb_default_read(void *opaque, hwaddr addr,
|
||||
uint64_t *data, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
qemu_log_mask(LOG_UNIMP, "Read of unassigned area of PPB: offset 0x%x\n",
|
||||
(uint32_t)addr);
|
||||
if (attrs.user) {
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
*data = 0;
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
static MemTxResult ppb_default_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
qemu_log_mask(LOG_UNIMP, "Write of unassigned area of PPB: offset 0x%x\n",
|
||||
(uint32_t)addr);
|
||||
if (attrs.user) {
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ppb_default_ops = {
|
||||
.read_with_attrs = ppb_default_read,
|
||||
.write_with_attrs = ppb_default_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid.min_access_size = 1,
|
||||
.valid.max_access_size = 8,
|
||||
};
|
||||
|
||||
static void armv7m_instance_init(Object *obj)
|
||||
{
|
||||
ARMv7MState *s = ARMV7M(obj);
|
||||
|
@ -137,10 +256,20 @@ static void armv7m_instance_init(Object *obj)
|
|||
object_property_add_alias(obj, "num-irq",
|
||||
OBJECT(&s->nvic), "num-irq");
|
||||
|
||||
object_initialize_child(obj, "systick-reg-ns", &s->systick[M_REG_NS],
|
||||
TYPE_SYSTICK);
|
||||
/*
|
||||
* We can't initialize the secure systick here, as we don't know
|
||||
* yet if we need it.
|
||||
*/
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(s->bitband); i++) {
|
||||
object_initialize_child(obj, "bitband[*]", &s->bitband[i],
|
||||
TYPE_BITBAND);
|
||||
}
|
||||
|
||||
s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk", NULL, NULL, 0);
|
||||
s->cpuclk = qdev_init_clock_in(DEVICE(obj), "cpuclk", NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static void armv7m_realize(DeviceState *dev, Error **errp)
|
||||
|
@ -223,13 +352,130 @@ static void armv7m_realize(DeviceState *dev, Error **errp)
|
|||
qdev_pass_gpios(DEVICE(&s->nvic), dev, "SYSRESETREQ");
|
||||
qdev_pass_gpios(DEVICE(&s->nvic), dev, "NMI");
|
||||
|
||||
/*
|
||||
* We map various devices into the container MR at their architected
|
||||
* addresses. In particular, we map everything corresponding to the
|
||||
* "System PPB" space. This is the range from 0xe0000000 to 0xe00fffff
|
||||
* and includes the NVIC, the System Control Space (system registers),
|
||||
* the systick timer, and for CPUs with the Security extension an NS
|
||||
* banked version of all of these.
|
||||
*
|
||||
* The default behaviour for unimplemented registers/ranges
|
||||
* (for instance the Data Watchpoint and Trace unit at 0xe0001000)
|
||||
* is to RAZ/WI for privileged access and BusFault for non-privileged
|
||||
* access.
|
||||
*
|
||||
* The NVIC and System Control Space (SCS) starts at 0xe000e000
|
||||
* and looks like this:
|
||||
* 0x004 - ICTR
|
||||
* 0x010 - 0xff - systick
|
||||
* 0x100..0x7ec - NVIC
|
||||
* 0x7f0..0xcff - Reserved
|
||||
* 0xd00..0xd3c - SCS registers
|
||||
* 0xd40..0xeff - Reserved or Not implemented
|
||||
* 0xf00 - STIR
|
||||
*
|
||||
* Some registers within this space are banked between security states.
|
||||
* In v8M there is a second range 0xe002e000..0xe002efff which is the
|
||||
* NonSecure alias SCS; secure accesses to this behave like NS accesses
|
||||
* to the main SCS range, and non-secure accesses (including when
|
||||
* the security extension is not implemented) are RAZ/WI.
|
||||
* Note that both the main SCS range and the alias range are defined
|
||||
* to be exempt from memory attribution (R_BLJT) and so the memory
|
||||
* transaction attribute always matches the current CPU security
|
||||
* state (attrs.secure == env->v7m.secure). In the v7m_sysreg_ns_ops
|
||||
* wrappers we change attrs.secure to indicate the NS access; so
|
||||
* generally code determining which banked register to use should
|
||||
* use attrs.secure; code determining actual behaviour of the system
|
||||
* should use env->v7m.secure.
|
||||
*
|
||||
* Within the PPB space, some MRs overlap, and the priority
|
||||
* of overlapping regions is:
|
||||
* - default region (for RAZ/WI and BusFault) : -1
|
||||
* - system register regions (provided by the NVIC) : 0
|
||||
* - systick : 1
|
||||
* This is because the systick device is a small block of registers
|
||||
* in the middle of the other system control registers.
|
||||
*/
|
||||
|
||||
memory_region_init_io(&s->defaultmem, OBJECT(s), &ppb_default_ops, s,
|
||||
"nvic-default", 0x100000);
|
||||
memory_region_add_subregion_overlap(&s->container, 0xe0000000,
|
||||
&s->defaultmem, -1);
|
||||
|
||||
/* Wire the NVIC up to the CPU */
|
||||
sbd = SYS_BUS_DEVICE(&s->nvic);
|
||||
sysbus_connect_irq(sbd, 0,
|
||||
qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ));
|
||||
|
||||
memory_region_add_subregion(&s->container, 0xe0000000,
|
||||
memory_region_add_subregion(&s->container, 0xe000e000,
|
||||
sysbus_mmio_get_region(sbd, 0));
|
||||
if (arm_feature(&s->cpu->env, ARM_FEATURE_V8)) {
|
||||
/* Create the NS alias region for the NVIC sysregs */
|
||||
memory_region_init_io(&s->sysreg_ns_mem, OBJECT(s),
|
||||
&v7m_sysreg_ns_ops,
|
||||
sysbus_mmio_get_region(sbd, 0),
|
||||
"nvic_sysregs_ns", 0x1000);
|
||||
memory_region_add_subregion(&s->container, 0xe002e000,
|
||||
&s->sysreg_ns_mem);
|
||||
}
|
||||
|
||||
/* Create and map the systick devices */
|
||||
qdev_connect_clock_in(DEVICE(&s->systick[M_REG_NS]), "refclk", s->refclk);
|
||||
qdev_connect_clock_in(DEVICE(&s->systick[M_REG_NS]), "cpuclk", s->cpuclk);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->systick[M_REG_NS]), errp)) {
|
||||
return;
|
||||
}
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->systick[M_REG_NS]), 0,
|
||||
qdev_get_gpio_in_named(DEVICE(&s->nvic),
|
||||
"systick-trigger", M_REG_NS));
|
||||
|
||||
if (arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY)) {
|
||||
/*
|
||||
* We couldn't init the secure systick device in instance_init
|
||||
* as we didn't know then if the CPU had the security extensions;
|
||||
* so we have to do it here.
|
||||
*/
|
||||
object_initialize_child(OBJECT(dev), "systick-reg-s",
|
||||
&s->systick[M_REG_S], TYPE_SYSTICK);
|
||||
qdev_connect_clock_in(DEVICE(&s->systick[M_REG_S]), "refclk",
|
||||
s->refclk);
|
||||
qdev_connect_clock_in(DEVICE(&s->systick[M_REG_S]), "cpuclk",
|
||||
s->cpuclk);
|
||||
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->systick[M_REG_S]), errp)) {
|
||||
return;
|
||||
}
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->systick[M_REG_S]), 0,
|
||||
qdev_get_gpio_in_named(DEVICE(&s->nvic),
|
||||
"systick-trigger", M_REG_S));
|
||||
}
|
||||
|
||||
memory_region_init_io(&s->systickmem, OBJECT(s),
|
||||
&v7m_systick_ops, s,
|
||||
"v7m_systick", 0xe0);
|
||||
|
||||
memory_region_add_subregion_overlap(&s->container, 0xe000e010,
|
||||
&s->systickmem, 1);
|
||||
if (arm_feature(&s->cpu->env, ARM_FEATURE_V8)) {
|
||||
memory_region_init_io(&s->systick_ns_mem, OBJECT(s),
|
||||
&v7m_sysreg_ns_ops, &s->systickmem,
|
||||
"v7m_systick_ns", 0xe0);
|
||||
memory_region_add_subregion_overlap(&s->container, 0xe002e010,
|
||||
&s->systick_ns_mem, 1);
|
||||
}
|
||||
|
||||
/* If the CPU has RAS support, create the RAS register block */
|
||||
if (cpu_isar_feature(aa32_ras, s->cpu)) {
|
||||
object_initialize_child(OBJECT(dev), "armv7m-ras",
|
||||
&s->ras, TYPE_ARMV7M_RAS);
|
||||
sbd = SYS_BUS_DEVICE(&s->ras);
|
||||
if (!sysbus_realize(sbd, errp)) {
|
||||
return;
|
||||
}
|
||||
memory_region_add_subregion_overlap(&s->container, 0xe0005000,
|
||||
sysbus_mmio_get_region(sbd, 0), 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(s->bitband); i++) {
|
||||
if (s->enable_bitband) {
|
||||
|
@ -269,11 +515,23 @@ static Property armv7m_properties[] = {
|
|||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_armv7m = {
|
||||
.name = "armv7m",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_CLOCK(refclk, SysTickState),
|
||||
VMSTATE_CLOCK(cpuclk, SysTickState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void armv7m_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = armv7m_realize;
|
||||
dc->vmsd = &vmstate_armv7m;
|
||||
device_class_set_props(dc, armv7m_properties);
|
||||
}
|
||||
|
||||
|
|
|
@ -86,6 +86,7 @@ struct MPS2MachineState {
|
|||
CMSDKAPBWatchdog watchdog;
|
||||
CMSDKAPBTimer timer[2];
|
||||
Clock *sysclk;
|
||||
Clock *refclk;
|
||||
};
|
||||
|
||||
#define TYPE_MPS2_MACHINE "mps2"
|
||||
|
@ -99,6 +100,15 @@ OBJECT_DECLARE_TYPE(MPS2MachineState, MPS2MachineClass, MPS2_MACHINE)
|
|||
/* Main SYSCLK frequency in Hz */
|
||||
#define SYSCLK_FRQ 25000000
|
||||
|
||||
/*
|
||||
* The Application Notes don't say anything about how the
|
||||
* systick reference clock is configured. (Quite possibly
|
||||
* they don't have one at all.) This 1MHz clock matches the
|
||||
* pre-existing behaviour that used to be hardcoded in the
|
||||
* armv7m_systick implementation.
|
||||
*/
|
||||
#define REFCLK_FRQ (1 * 1000 * 1000)
|
||||
|
||||
/* Initialize the auxiliary RAM region @mr and map it into
|
||||
* the memory map at @base.
|
||||
*/
|
||||
|
@ -146,6 +156,9 @@ static void mps2_common_init(MachineState *machine)
|
|||
mms->sysclk = clock_new(OBJECT(machine), "SYSCLK");
|
||||
clock_set_hz(mms->sysclk, SYSCLK_FRQ);
|
||||
|
||||
mms->refclk = clock_new(OBJECT(machine), "REFCLK");
|
||||
clock_set_hz(mms->refclk, REFCLK_FRQ);
|
||||
|
||||
/* The FPGA images have an odd combination of different RAMs,
|
||||
* because in hardware they are different implementations and
|
||||
* connected to different buses, giving varying performance/size
|
||||
|
@ -223,6 +236,8 @@ static void mps2_common_init(MachineState *machine)
|
|||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
qdev_connect_clock_in(armv7m, "cpuclk", mms->sysclk);
|
||||
qdev_connect_clock_in(armv7m, "refclk", mms->refclk);
|
||||
qdev_prop_set_string(armv7m, "cpu-type", machine->cpu_type);
|
||||
qdev_prop_set_bit(armv7m, "enable-bitband", true);
|
||||
object_property_set_link(OBJECT(&mms->armv7m), "memory",
|
||||
|
@ -424,8 +439,6 @@ static void mps2_common_init(MachineState *machine)
|
|||
qdev_get_gpio_in(armv7m,
|
||||
mmc->fpga_type == FPGA_AN511 ? 47 : 13));
|
||||
|
||||
system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ;
|
||||
|
||||
armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename,
|
||||
0x400000);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "hw/char/serial.h"
|
||||
#include "hw/arm/msf2-soc.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
#define MSF2_TIMER_BASE 0x40004000
|
||||
|
@ -73,6 +74,9 @@ static void m2sxxx_soc_initfn(Object *obj)
|
|||
}
|
||||
|
||||
object_initialize_child(obj, "emac", &s->emac, TYPE_MSS_EMAC);
|
||||
|
||||
s->m3clk = qdev_init_clock_in(DEVICE(obj), "m3clk", NULL, NULL, 0);
|
||||
s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk", NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
|
@ -83,11 +87,34 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp)
|
|||
int i;
|
||||
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *nvm = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *nvm_alias = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *sram = g_new(MemoryRegion, 1);
|
||||
|
||||
memory_region_init_rom(nvm, OBJECT(dev_soc), "MSF2.eNVM", s->envm_size,
|
||||
if (!clock_has_source(s->m3clk)) {
|
||||
error_setg(errp, "m3clk must be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We use s->refclk internally and only define it with qdev_init_clock_in()
|
||||
* so it is correctly parented and not leaked on an init/deinit; it is not
|
||||
* intended as an externally exposed clock.
|
||||
*/
|
||||
if (clock_has_source(s->refclk)) {
|
||||
error_setg(errp, "refclk must not be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: ideally we should model the SoC SYSTICK_CR register at 0xe0042038,
|
||||
* which allows the guest to program the divisor between the m3clk and
|
||||
* the systick refclk to either /4, /8, /16 or /32, as well as setting
|
||||
* the value the guest can read in the STCALIB register. Currently we
|
||||
* implement the divisor as a fixed /32, which matches the reset value
|
||||
* of SYSTICK_CR.
|
||||
*/
|
||||
clock_set_mul_div(s->refclk, 32, 1);
|
||||
clock_set_source(s->refclk, s->m3clk);
|
||||
|
||||
memory_region_init_rom(&s->nvm, OBJECT(dev_soc), "MSF2.eNVM", s->envm_size,
|
||||
&error_fatal);
|
||||
/*
|
||||
* On power-on, the eNVM region 0x60000000 is automatically
|
||||
|
@ -95,34 +122,28 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp)
|
|||
* start address (0x0). We do not support remapping other eNVM,
|
||||
* eSRAM and DDR regions by guest(via Sysreg) currently.
|
||||
*/
|
||||
memory_region_init_alias(nvm_alias, OBJECT(dev_soc), "MSF2.eNVM", nvm, 0,
|
||||
s->envm_size);
|
||||
memory_region_init_alias(&s->nvm_alias, OBJECT(dev_soc), "MSF2.eNVM",
|
||||
&s->nvm, 0, s->envm_size);
|
||||
|
||||
memory_region_add_subregion(system_memory, ENVM_BASE_ADDRESS, nvm);
|
||||
memory_region_add_subregion(system_memory, 0, nvm_alias);
|
||||
memory_region_add_subregion(system_memory, ENVM_BASE_ADDRESS, &s->nvm);
|
||||
memory_region_add_subregion(system_memory, 0, &s->nvm_alias);
|
||||
|
||||
memory_region_init_ram(sram, NULL, "MSF2.eSRAM", s->esram_size,
|
||||
memory_region_init_ram(&s->sram, NULL, "MSF2.eSRAM", s->esram_size,
|
||||
&error_fatal);
|
||||
memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram);
|
||||
memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, &s->sram);
|
||||
|
||||
armv7m = DEVICE(&s->armv7m);
|
||||
qdev_prop_set_uint32(armv7m, "num-irq", 81);
|
||||
qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type);
|
||||
qdev_prop_set_bit(armv7m, "enable-bitband", true);
|
||||
qdev_connect_clock_in(armv7m, "cpuclk", s->m3clk);
|
||||
qdev_connect_clock_in(armv7m, "refclk", s->refclk);
|
||||
object_property_set_link(OBJECT(&s->armv7m), "memory",
|
||||
OBJECT(get_system_memory()), &error_abort);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!s->m3clk) {
|
||||
error_setg(errp, "Invalid m3clk value");
|
||||
error_append_hint(errp, "m3clk can not be zero\n");
|
||||
return;
|
||||
}
|
||||
|
||||
system_clock_scale = NANOSECONDS_PER_SECOND / s->m3clk;
|
||||
|
||||
for (i = 0; i < MSF2_NUM_UARTS; i++) {
|
||||
if (serial_hd(i)) {
|
||||
serial_mm_init(get_system_memory(), uart_addr[i], 2,
|
||||
|
@ -132,8 +153,13 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp)
|
|||
}
|
||||
|
||||
dev = DEVICE(&s->timer);
|
||||
/* APB0 clock is the timer input clock */
|
||||
qdev_prop_set_uint32(dev, "clock-frequency", s->m3clk / s->apb0div);
|
||||
/*
|
||||
* APB0 clock is the timer input clock.
|
||||
* TODO: ideally the MSF2 timer device should use a Clock rather than a
|
||||
* clock-frequency integer property.
|
||||
*/
|
||||
qdev_prop_set_uint32(dev, "clock-frequency",
|
||||
clock_get_hz(s->m3clk) / s->apb0div);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->timer), errp)) {
|
||||
return;
|
||||
}
|
||||
|
@ -210,8 +236,6 @@ static Property m2sxxx_soc_properties[] = {
|
|||
DEFINE_PROP_UINT64("eNVM-size", MSF2State, envm_size, MSF2_ENVM_MAX_SIZE),
|
||||
DEFINE_PROP_UINT64("eSRAM-size", MSF2State, esram_size,
|
||||
MSF2_ESRAM_MAX_SIZE),
|
||||
/* Libero GUI shows 100Mhz as default for clocks */
|
||||
DEFINE_PROP_UINT32("m3clk", MSF2State, m3clk, 100 * 1000000),
|
||||
/* default divisors in Libero GUI */
|
||||
DEFINE_PROP_UINT8("apb0div", MSF2State, apb0div, 2),
|
||||
DEFINE_PROP_UINT8("apb1div", MSF2State, apb1div, 2),
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "hw/boards.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/arm/boot.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/arm/msf2-soc.h"
|
||||
|
||||
|
@ -49,6 +50,7 @@ static void emcraft_sf2_s2s010_init(MachineState *machine)
|
|||
BusState *spi_bus;
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
MemoryRegion *ddr = g_new(MemoryRegion, 1);
|
||||
Clock *m3clk;
|
||||
|
||||
if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) {
|
||||
error_report("This board can only be used with CPU %s",
|
||||
|
@ -72,7 +74,10 @@ static void emcraft_sf2_s2s010_init(MachineState *machine)
|
|||
* in Libero. CPU clock is divided by APB0 and APB1 divisors for
|
||||
* peripherals. Emcraft's SoM kit comes with these settings by default.
|
||||
*/
|
||||
qdev_prop_set_uint32(dev, "m3clk", 142 * 1000000);
|
||||
/* This clock doesn't need migration because it is fixed-frequency */
|
||||
m3clk = clock_new(OBJECT(machine), "m3clk");
|
||||
clock_set_hz(m3clk, 142 * 1000000);
|
||||
qdev_connect_clock_in(dev, "m3clk", m3clk);
|
||||
qdev_prop_set_uint32(dev, "apb0div", 2);
|
||||
qdev_prop_set_uint32(dev, "apb1div", 2);
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "qapi/error.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/arm/stm32f205_soc.h"
|
||||
#include "hw/arm/boot.h"
|
||||
|
@ -36,16 +37,15 @@
|
|||
static void netduino2_init(MachineState *machine)
|
||||
{
|
||||
DeviceState *dev;
|
||||
Clock *sysclk;
|
||||
|
||||
/*
|
||||
* TODO: ideally we would model the SoC RCC and let it handle
|
||||
* system_clock_scale, including its ability to define different
|
||||
* possible SYSCLK sources.
|
||||
*/
|
||||
system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ;
|
||||
/* This clock doesn't need migration because it is fixed-frequency */
|
||||
sysclk = clock_new(OBJECT(machine), "SYSCLK");
|
||||
clock_set_hz(sysclk, SYSCLK_FRQ);
|
||||
|
||||
dev = qdev_new(TYPE_STM32F205_SOC);
|
||||
qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3"));
|
||||
qdev_connect_clock_in(dev, "sysclk", sysclk);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
|
||||
armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename,
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "qapi/error.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/arm/stm32f405_soc.h"
|
||||
#include "hw/arm/boot.h"
|
||||
|
@ -36,16 +37,15 @@
|
|||
static void netduinoplus2_init(MachineState *machine)
|
||||
{
|
||||
DeviceState *dev;
|
||||
Clock *sysclk;
|
||||
|
||||
/*
|
||||
* TODO: ideally we would model the SoC RCC and let it handle
|
||||
* system_clock_scale, including its ability to define different
|
||||
* possible SYSCLK sources.
|
||||
*/
|
||||
system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ;
|
||||
/* This clock doesn't need migration because it is fixed-frequency */
|
||||
sysclk = clock_new(OBJECT(machine), "SYSCLK");
|
||||
clock_set_hz(sysclk, SYSCLK_FRQ);
|
||||
|
||||
dev = qdev_new(TYPE_STM32F405_SOC);
|
||||
qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4"));
|
||||
qdev_connect_clock_in(dev, "sysclk", sysclk);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
|
||||
armv7m_load_kernel(ARM_CPU(first_cpu),
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "qapi/error.h"
|
||||
#include "hw/arm/boot.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "qemu/log.h"
|
||||
|
||||
|
@ -66,7 +67,22 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp)
|
|||
return;
|
||||
}
|
||||
|
||||
system_clock_scale = NANOSECONDS_PER_SECOND / HCLK_FRQ;
|
||||
/*
|
||||
* HCLK on this SoC is fixed, so we set up sysclk ourselves and
|
||||
* the board shouldn't connect it.
|
||||
*/
|
||||
if (clock_has_source(s->sysclk)) {
|
||||
error_setg(errp, "sysclk clock must not be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
/* This clock doesn't need migration because it is fixed-frequency */
|
||||
clock_set_hz(s->sysclk, HCLK_FRQ);
|
||||
qdev_connect_clock_in(DEVICE(&s->cpu), "cpuclk", s->sysclk);
|
||||
/*
|
||||
* This SoC has no systick device, so don't connect refclk.
|
||||
* TODO: model the lack of systick (currently the armv7m object
|
||||
* will always provide one).
|
||||
*/
|
||||
|
||||
object_property_set_link(OBJECT(&s->cpu), "memory", OBJECT(&s->container),
|
||||
&error_abort);
|
||||
|
@ -191,6 +207,8 @@ static void nrf51_soc_init(Object *obj)
|
|||
TYPE_NRF51_TIMER);
|
||||
|
||||
}
|
||||
|
||||
s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static Property nrf51_soc_properties[] = {
|
||||
|
|
|
@ -340,7 +340,6 @@ static void raspi2b_machine_class_init(ObjectClass *oc, void *data)
|
|||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc);
|
||||
|
||||
mc->alias = "raspi2";
|
||||
rmc->board_rev = 0xa21041;
|
||||
raspi_machine_class_common_init(mc, rmc->board_rev);
|
||||
};
|
||||
|
@ -360,7 +359,6 @@ static void raspi3b_machine_class_init(ObjectClass *oc, void *data)
|
|||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc);
|
||||
|
||||
mc->alias = "raspi3";
|
||||
rmc->board_rev = 0xa02082;
|
||||
raspi_machine_class_common_init(mc, rmc->board_rev);
|
||||
};
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "hw/watchdog/cmsdk-apb-watchdog.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "hw/timer/stellaris-gptm.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
|
@ -55,306 +56,6 @@ typedef const struct {
|
|||
uint32_t peripherals;
|
||||
} stellaris_board_info;
|
||||
|
||||
/* General purpose timer module. */
|
||||
|
||||
#define TYPE_STELLARIS_GPTM "stellaris-gptm"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(gptm_state, STELLARIS_GPTM)
|
||||
|
||||
struct gptm_state {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
uint32_t config;
|
||||
uint32_t mode[2];
|
||||
uint32_t control;
|
||||
uint32_t state;
|
||||
uint32_t mask;
|
||||
uint32_t load[2];
|
||||
uint32_t match[2];
|
||||
uint32_t prescale[2];
|
||||
uint32_t match_prescale[2];
|
||||
uint32_t rtc;
|
||||
int64_t tick[2];
|
||||
struct gptm_state *opaque[2];
|
||||
QEMUTimer *timer[2];
|
||||
/* The timers have an alternate output used to trigger the ADC. */
|
||||
qemu_irq trigger;
|
||||
qemu_irq irq;
|
||||
};
|
||||
|
||||
static void gptm_update_irq(gptm_state *s)
|
||||
{
|
||||
int level;
|
||||
level = (s->state & s->mask) != 0;
|
||||
qemu_set_irq(s->irq, level);
|
||||
}
|
||||
|
||||
static void gptm_stop(gptm_state *s, int n)
|
||||
{
|
||||
timer_del(s->timer[n]);
|
||||
}
|
||||
|
||||
static void gptm_reload(gptm_state *s, int n, int reset)
|
||||
{
|
||||
int64_t tick;
|
||||
if (reset)
|
||||
tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
else
|
||||
tick = s->tick[n];
|
||||
|
||||
if (s->config == 0) {
|
||||
/* 32-bit CountDown. */
|
||||
uint32_t count;
|
||||
count = s->load[0] | (s->load[1] << 16);
|
||||
tick += (int64_t)count * system_clock_scale;
|
||||
} else if (s->config == 1) {
|
||||
/* 32-bit RTC. 1Hz tick. */
|
||||
tick += NANOSECONDS_PER_SECOND;
|
||||
} else if (s->mode[n] == 0xa) {
|
||||
/* PWM mode. Not implemented. */
|
||||
} else {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"GPTM: 16-bit timer mode unimplemented: 0x%x\n",
|
||||
s->mode[n]);
|
||||
return;
|
||||
}
|
||||
s->tick[n] = tick;
|
||||
timer_mod(s->timer[n], tick);
|
||||
}
|
||||
|
||||
static void gptm_tick(void *opaque)
|
||||
{
|
||||
gptm_state **p = (gptm_state **)opaque;
|
||||
gptm_state *s;
|
||||
int n;
|
||||
|
||||
s = *p;
|
||||
n = p - s->opaque;
|
||||
if (s->config == 0) {
|
||||
s->state |= 1;
|
||||
if ((s->control & 0x20)) {
|
||||
/* Output trigger. */
|
||||
qemu_irq_pulse(s->trigger);
|
||||
}
|
||||
if (s->mode[0] & 1) {
|
||||
/* One-shot. */
|
||||
s->control &= ~1;
|
||||
} else {
|
||||
/* Periodic. */
|
||||
gptm_reload(s, 0, 0);
|
||||
}
|
||||
} else if (s->config == 1) {
|
||||
/* RTC. */
|
||||
uint32_t match;
|
||||
s->rtc++;
|
||||
match = s->match[0] | (s->match[1] << 16);
|
||||
if (s->rtc > match)
|
||||
s->rtc = 0;
|
||||
if (s->rtc == 0) {
|
||||
s->state |= 8;
|
||||
}
|
||||
gptm_reload(s, 0, 0);
|
||||
} else if (s->mode[n] == 0xa) {
|
||||
/* PWM mode. Not implemented. */
|
||||
} else {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"GPTM: 16-bit timer mode unimplemented: 0x%x\n",
|
||||
s->mode[n]);
|
||||
}
|
||||
gptm_update_irq(s);
|
||||
}
|
||||
|
||||
static uint64_t gptm_read(void *opaque, hwaddr offset,
|
||||
unsigned size)
|
||||
{
|
||||
gptm_state *s = (gptm_state *)opaque;
|
||||
|
||||
switch (offset) {
|
||||
case 0x00: /* CFG */
|
||||
return s->config;
|
||||
case 0x04: /* TAMR */
|
||||
return s->mode[0];
|
||||
case 0x08: /* TBMR */
|
||||
return s->mode[1];
|
||||
case 0x0c: /* CTL */
|
||||
return s->control;
|
||||
case 0x18: /* IMR */
|
||||
return s->mask;
|
||||
case 0x1c: /* RIS */
|
||||
return s->state;
|
||||
case 0x20: /* MIS */
|
||||
return s->state & s->mask;
|
||||
case 0x24: /* CR */
|
||||
return 0;
|
||||
case 0x28: /* TAILR */
|
||||
return s->load[0] | ((s->config < 4) ? (s->load[1] << 16) : 0);
|
||||
case 0x2c: /* TBILR */
|
||||
return s->load[1];
|
||||
case 0x30: /* TAMARCHR */
|
||||
return s->match[0] | ((s->config < 4) ? (s->match[1] << 16) : 0);
|
||||
case 0x34: /* TBMATCHR */
|
||||
return s->match[1];
|
||||
case 0x38: /* TAPR */
|
||||
return s->prescale[0];
|
||||
case 0x3c: /* TBPR */
|
||||
return s->prescale[1];
|
||||
case 0x40: /* TAPMR */
|
||||
return s->match_prescale[0];
|
||||
case 0x44: /* TBPMR */
|
||||
return s->match_prescale[1];
|
||||
case 0x48: /* TAR */
|
||||
if (s->config == 1) {
|
||||
return s->rtc;
|
||||
}
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"GPTM: read of TAR but timer read not supported\n");
|
||||
return 0;
|
||||
case 0x4c: /* TBR */
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"GPTM: read of TBR but timer read not supported\n");
|
||||
return 0;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"GPTM: read at bad offset 0x02%" HWADDR_PRIx "\n",
|
||||
offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void gptm_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
gptm_state *s = (gptm_state *)opaque;
|
||||
uint32_t oldval;
|
||||
|
||||
/* The timers should be disabled before changing the configuration.
|
||||
We take advantage of this and defer everything until the timer
|
||||
is enabled. */
|
||||
switch (offset) {
|
||||
case 0x00: /* CFG */
|
||||
s->config = value;
|
||||
break;
|
||||
case 0x04: /* TAMR */
|
||||
s->mode[0] = value;
|
||||
break;
|
||||
case 0x08: /* TBMR */
|
||||
s->mode[1] = value;
|
||||
break;
|
||||
case 0x0c: /* CTL */
|
||||
oldval = s->control;
|
||||
s->control = value;
|
||||
/* TODO: Implement pause. */
|
||||
if ((oldval ^ value) & 1) {
|
||||
if (value & 1) {
|
||||
gptm_reload(s, 0, 1);
|
||||
} else {
|
||||
gptm_stop(s, 0);
|
||||
}
|
||||
}
|
||||
if (((oldval ^ value) & 0x100) && s->config >= 4) {
|
||||
if (value & 0x100) {
|
||||
gptm_reload(s, 1, 1);
|
||||
} else {
|
||||
gptm_stop(s, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x18: /* IMR */
|
||||
s->mask = value & 0x77;
|
||||
gptm_update_irq(s);
|
||||
break;
|
||||
case 0x24: /* CR */
|
||||
s->state &= ~value;
|
||||
break;
|
||||
case 0x28: /* TAILR */
|
||||
s->load[0] = value & 0xffff;
|
||||
if (s->config < 4) {
|
||||
s->load[1] = value >> 16;
|
||||
}
|
||||
break;
|
||||
case 0x2c: /* TBILR */
|
||||
s->load[1] = value & 0xffff;
|
||||
break;
|
||||
case 0x30: /* TAMARCHR */
|
||||
s->match[0] = value & 0xffff;
|
||||
if (s->config < 4) {
|
||||
s->match[1] = value >> 16;
|
||||
}
|
||||
break;
|
||||
case 0x34: /* TBMATCHR */
|
||||
s->match[1] = value >> 16;
|
||||
break;
|
||||
case 0x38: /* TAPR */
|
||||
s->prescale[0] = value;
|
||||
break;
|
||||
case 0x3c: /* TBPR */
|
||||
s->prescale[1] = value;
|
||||
break;
|
||||
case 0x40: /* TAPMR */
|
||||
s->match_prescale[0] = value;
|
||||
break;
|
||||
case 0x44: /* TBPMR */
|
||||
s->match_prescale[0] = value;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"GPTM: write at bad offset 0x02%" HWADDR_PRIx "\n",
|
||||
offset);
|
||||
}
|
||||
gptm_update_irq(s);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps gptm_ops = {
|
||||
.read = gptm_read,
|
||||
.write = gptm_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_stellaris_gptm = {
|
||||
.name = "stellaris_gptm",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(config, gptm_state),
|
||||
VMSTATE_UINT32_ARRAY(mode, gptm_state, 2),
|
||||
VMSTATE_UINT32(control, gptm_state),
|
||||
VMSTATE_UINT32(state, gptm_state),
|
||||
VMSTATE_UINT32(mask, gptm_state),
|
||||
VMSTATE_UNUSED(8),
|
||||
VMSTATE_UINT32_ARRAY(load, gptm_state, 2),
|
||||
VMSTATE_UINT32_ARRAY(match, gptm_state, 2),
|
||||
VMSTATE_UINT32_ARRAY(prescale, gptm_state, 2),
|
||||
VMSTATE_UINT32_ARRAY(match_prescale, gptm_state, 2),
|
||||
VMSTATE_UINT32(rtc, gptm_state),
|
||||
VMSTATE_INT64_ARRAY(tick, gptm_state, 2),
|
||||
VMSTATE_TIMER_PTR_ARRAY(timer, gptm_state, 2),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void stellaris_gptm_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
gptm_state *s = STELLARIS_GPTM(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
qdev_init_gpio_out(dev, &s->trigger, 1);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &gptm_ops, s,
|
||||
"gptm", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
|
||||
s->opaque[0] = s->opaque[1] = s;
|
||||
}
|
||||
|
||||
static void stellaris_gptm_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
gptm_state *s = STELLARIS_GPTM(dev);
|
||||
s->timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[0]);
|
||||
s->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[1]);
|
||||
}
|
||||
|
||||
/* System controller. */
|
||||
|
||||
#define TYPE_STELLARIS_SYS "stellaris-sys"
|
||||
|
@ -562,17 +263,18 @@ static bool ssys_use_rcc2(ssys_state *s)
|
|||
*/
|
||||
static void ssys_calculate_system_clock(ssys_state *s, bool propagate_clock)
|
||||
{
|
||||
int period_ns;
|
||||
/*
|
||||
* SYSDIV field specifies divisor: 0 == /1, 1 == /2, etc. Input
|
||||
* clock is 200MHz, which is a period of 5 ns. Dividing the clock
|
||||
* frequency by X is the same as multiplying the period by X.
|
||||
*/
|
||||
if (ssys_use_rcc2(s)) {
|
||||
system_clock_scale = 5 * (((s->rcc2 >> 23) & 0x3f) + 1);
|
||||
period_ns = 5 * (((s->rcc2 >> 23) & 0x3f) + 1);
|
||||
} else {
|
||||
system_clock_scale = 5 * (((s->rcc >> 23) & 0xf) + 1);
|
||||
period_ns = 5 * (((s->rcc >> 23) & 0xf) + 1);
|
||||
}
|
||||
clock_set_ns(s->sysclk, system_clock_scale);
|
||||
clock_set_ns(s->sysclk, period_ns);
|
||||
if (propagate_clock) {
|
||||
clock_propagate(s->sysclk);
|
||||
}
|
||||
|
@ -755,33 +457,6 @@ static void stellaris_sys_instance_init(Object *obj)
|
|||
s->sysclk = qdev_init_clock_out(DEVICE(s), "SYSCLK");
|
||||
}
|
||||
|
||||
static DeviceState *stellaris_sys_init(uint32_t base, qemu_irq irq,
|
||||
stellaris_board_info *board,
|
||||
uint8_t *macaddr)
|
||||
{
|
||||
DeviceState *dev = qdev_new(TYPE_STELLARIS_SYS);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
|
||||
/* Most devices come preprogrammed with a MAC address in the user data. */
|
||||
qdev_prop_set_uint32(dev, "user0",
|
||||
macaddr[0] | (macaddr[1] << 8) | (macaddr[2] << 16));
|
||||
qdev_prop_set_uint32(dev, "user1",
|
||||
macaddr[3] | (macaddr[4] << 8) | (macaddr[5] << 16));
|
||||
qdev_prop_set_uint32(dev, "did0", board->did0);
|
||||
qdev_prop_set_uint32(dev, "did1", board->did1);
|
||||
qdev_prop_set_uint32(dev, "dc0", board->dc0);
|
||||
qdev_prop_set_uint32(dev, "dc1", board->dc1);
|
||||
qdev_prop_set_uint32(dev, "dc2", board->dc2);
|
||||
qdev_prop_set_uint32(dev, "dc3", board->dc3);
|
||||
qdev_prop_set_uint32(dev, "dc4", board->dc4);
|
||||
|
||||
sysbus_realize_and_unref(sbd, &error_fatal);
|
||||
sysbus_mmio_map(sbd, 0, base);
|
||||
sysbus_connect_irq(sbd, 0, irq);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
/* I2C controller. */
|
||||
|
||||
#define TYPE_STELLARIS_I2C "stellaris-i2c"
|
||||
|
@ -1349,6 +1024,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board)
|
|||
DeviceState *ssys_dev;
|
||||
int i;
|
||||
int j;
|
||||
const uint8_t *macaddr;
|
||||
|
||||
MemoryRegion *sram = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *flash = g_new(MemoryRegion, 1);
|
||||
|
@ -1366,15 +1042,42 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board)
|
|||
&error_fatal);
|
||||
memory_region_add_subregion(system_memory, 0x20000000, sram);
|
||||
|
||||
/*
|
||||
* Create the system-registers object early, because we will
|
||||
* need its sysclk output.
|
||||
*/
|
||||
ssys_dev = qdev_new(TYPE_STELLARIS_SYS);
|
||||
/* Most devices come preprogrammed with a MAC address in the user data. */
|
||||
macaddr = nd_table[0].macaddr.a;
|
||||
qdev_prop_set_uint32(ssys_dev, "user0",
|
||||
macaddr[0] | (macaddr[1] << 8) | (macaddr[2] << 16));
|
||||
qdev_prop_set_uint32(ssys_dev, "user1",
|
||||
macaddr[3] | (macaddr[4] << 8) | (macaddr[5] << 16));
|
||||
qdev_prop_set_uint32(ssys_dev, "did0", board->did0);
|
||||
qdev_prop_set_uint32(ssys_dev, "did1", board->did1);
|
||||
qdev_prop_set_uint32(ssys_dev, "dc0", board->dc0);
|
||||
qdev_prop_set_uint32(ssys_dev, "dc1", board->dc1);
|
||||
qdev_prop_set_uint32(ssys_dev, "dc2", board->dc2);
|
||||
qdev_prop_set_uint32(ssys_dev, "dc3", board->dc3);
|
||||
qdev_prop_set_uint32(ssys_dev, "dc4", board->dc4);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(ssys_dev), &error_fatal);
|
||||
|
||||
nvic = qdev_new(TYPE_ARMV7M);
|
||||
qdev_prop_set_uint32(nvic, "num-irq", NUM_IRQ_LINES);
|
||||
qdev_prop_set_string(nvic, "cpu-type", ms->cpu_type);
|
||||
qdev_prop_set_bit(nvic, "enable-bitband", true);
|
||||
qdev_connect_clock_in(nvic, "cpuclk",
|
||||
qdev_get_clock_out(ssys_dev, "SYSCLK"));
|
||||
/* This SoC does not connect the systick reference clock */
|
||||
object_property_set_link(OBJECT(nvic), "memory",
|
||||
OBJECT(get_system_memory()), &error_abort);
|
||||
/* This will exit with an error if the user passed us a bad cpu_type */
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(nvic), &error_fatal);
|
||||
|
||||
/* Now we can wire up the IRQ and MMIO of the system registers */
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(ssys_dev), 0, 0x400fe000);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(ssys_dev), 0, qdev_get_gpio_in(nvic, 28));
|
||||
|
||||
if (board->dc1 & (1 << 16)) {
|
||||
dev = sysbus_create_varargs(TYPE_STELLARIS_ADC, 0x40038000,
|
||||
qdev_get_gpio_in(nvic, 14),
|
||||
|
@ -1388,19 +1091,21 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board)
|
|||
}
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (board->dc2 & (0x10000 << i)) {
|
||||
dev = sysbus_create_simple(TYPE_STELLARIS_GPTM,
|
||||
0x40030000 + i * 0x1000,
|
||||
qdev_get_gpio_in(nvic, timer_irq[i]));
|
||||
SysBusDevice *sbd;
|
||||
|
||||
dev = qdev_new(TYPE_STELLARIS_GPTM);
|
||||
sbd = SYS_BUS_DEVICE(dev);
|
||||
qdev_connect_clock_in(dev, "clk",
|
||||
qdev_get_clock_out(ssys_dev, "SYSCLK"));
|
||||
sysbus_realize_and_unref(sbd, &error_fatal);
|
||||
sysbus_mmio_map(sbd, 0, 0x40030000 + i * 0x1000);
|
||||
sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(nvic, timer_irq[i]));
|
||||
/* TODO: This is incorrect, but we get away with it because
|
||||
the ADC output is only ever pulsed. */
|
||||
qdev_connect_gpio_out(dev, 0, adc);
|
||||
}
|
||||
}
|
||||
|
||||
ssys_dev = stellaris_sys_init(0x400fe000, qdev_get_gpio_in(nvic, 28),
|
||||
board, nd_table[0].macaddr.a);
|
||||
|
||||
|
||||
if (board->dc1 & (1 << 3)) { /* watchdog present */
|
||||
dev = qdev_new(TYPE_LUMINARY_WATCHDOG);
|
||||
|
||||
|
@ -1642,22 +1347,6 @@ static const TypeInfo stellaris_i2c_info = {
|
|||
.class_init = stellaris_i2c_class_init,
|
||||
};
|
||||
|
||||
static void stellaris_gptm_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->vmsd = &vmstate_stellaris_gptm;
|
||||
dc->realize = stellaris_gptm_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo stellaris_gptm_info = {
|
||||
.name = TYPE_STELLARIS_GPTM,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(gptm_state),
|
||||
.instance_init = stellaris_gptm_init,
|
||||
.class_init = stellaris_gptm_class_init,
|
||||
};
|
||||
|
||||
static void stellaris_adc_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
@ -1696,7 +1385,6 @@ static const TypeInfo stellaris_sys_info = {
|
|||
static void stellaris_register_types(void)
|
||||
{
|
||||
type_register_static(&stellaris_i2c_info);
|
||||
type_register_static(&stellaris_gptm_info);
|
||||
type_register_static(&stellaris_adc_info);
|
||||
type_register_static(&stellaris_sys_info);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "exec/address-spaces.h"
|
||||
#include "hw/arm/stm32f100_soc.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
|
@ -57,6 +58,9 @@ static void stm32f100_soc_initfn(Object *obj)
|
|||
for (i = 0; i < STM_NUM_SPIS; i++) {
|
||||
object_initialize_child(obj, "spi[*]", &s->spi[i], TYPE_STM32F2XX_SPI);
|
||||
}
|
||||
|
||||
s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
|
||||
s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
|
@ -67,31 +71,54 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
|
|||
int i;
|
||||
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *sram = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *flash = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *flash_alias = g_new(MemoryRegion, 1);
|
||||
|
||||
/*
|
||||
* We use s->refclk internally and only define it with qdev_init_clock_in()
|
||||
* so it is correctly parented and not leaked on an init/deinit; it is not
|
||||
* intended as an externally exposed clock.
|
||||
*/
|
||||
if (clock_has_source(s->refclk)) {
|
||||
error_setg(errp, "refclk clock must not be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!clock_has_source(s->sysclk)) {
|
||||
error_setg(errp, "sysclk clock must be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: ideally we should model the SoC RCC and its ability to
|
||||
* change the sysclk frequency and define different sysclk sources.
|
||||
*/
|
||||
|
||||
/* The refclk always runs at frequency HCLK / 8 */
|
||||
clock_set_mul_div(s->refclk, 8, 1);
|
||||
clock_set_source(s->refclk, s->sysclk);
|
||||
|
||||
/*
|
||||
* Init flash region
|
||||
* Flash starts at 0x08000000 and then is aliased to boot memory at 0x0
|
||||
*/
|
||||
memory_region_init_rom(flash, OBJECT(dev_soc), "STM32F100.flash",
|
||||
memory_region_init_rom(&s->flash, OBJECT(dev_soc), "STM32F100.flash",
|
||||
FLASH_SIZE, &error_fatal);
|
||||
memory_region_init_alias(flash_alias, OBJECT(dev_soc),
|
||||
"STM32F100.flash.alias", flash, 0, FLASH_SIZE);
|
||||
memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, flash);
|
||||
memory_region_add_subregion(system_memory, 0, flash_alias);
|
||||
memory_region_init_alias(&s->flash_alias, OBJECT(dev_soc),
|
||||
"STM32F100.flash.alias", &s->flash, 0, FLASH_SIZE);
|
||||
memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash);
|
||||
memory_region_add_subregion(system_memory, 0, &s->flash_alias);
|
||||
|
||||
/* Init SRAM region */
|
||||
memory_region_init_ram(sram, NULL, "STM32F100.sram", SRAM_SIZE,
|
||||
memory_region_init_ram(&s->sram, NULL, "STM32F100.sram", SRAM_SIZE,
|
||||
&error_fatal);
|
||||
memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram);
|
||||
memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, &s->sram);
|
||||
|
||||
/* Init ARMv7m */
|
||||
armv7m = DEVICE(&s->armv7m);
|
||||
qdev_prop_set_uint32(armv7m, "num-irq", 61);
|
||||
qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type);
|
||||
qdev_prop_set_bit(armv7m, "enable-bitband", true);
|
||||
qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);
|
||||
qdev_connect_clock_in(armv7m, "refclk", s->refclk);
|
||||
object_property_set_link(OBJECT(&s->armv7m), "memory",
|
||||
OBJECT(get_system_memory()), &error_abort);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) {
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "exec/address-spaces.h"
|
||||
#include "hw/arm/stm32f205_soc.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
/* At the moment only Timer 2 to 5 are modelled */
|
||||
|
@ -74,6 +75,9 @@ static void stm32f205_soc_initfn(Object *obj)
|
|||
for (i = 0; i < STM_NUM_SPIS; i++) {
|
||||
object_initialize_child(obj, "spi[*]", &s->spi[i], TYPE_STM32F2XX_SPI);
|
||||
}
|
||||
|
||||
s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
|
||||
s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
|
@ -84,26 +88,49 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
|
|||
int i;
|
||||
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *sram = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *flash = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *flash_alias = g_new(MemoryRegion, 1);
|
||||
|
||||
memory_region_init_rom(flash, OBJECT(dev_soc), "STM32F205.flash",
|
||||
/*
|
||||
* We use s->refclk internally and only define it with qdev_init_clock_in()
|
||||
* so it is correctly parented and not leaked on an init/deinit; it is not
|
||||
* intended as an externally exposed clock.
|
||||
*/
|
||||
if (clock_has_source(s->refclk)) {
|
||||
error_setg(errp, "refclk clock must not be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!clock_has_source(s->sysclk)) {
|
||||
error_setg(errp, "sysclk clock must be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: ideally we should model the SoC RCC and its ability to
|
||||
* change the sysclk frequency and define different sysclk sources.
|
||||
*/
|
||||
|
||||
/* The refclk always runs at frequency HCLK / 8 */
|
||||
clock_set_mul_div(s->refclk, 8, 1);
|
||||
clock_set_source(s->refclk, s->sysclk);
|
||||
|
||||
memory_region_init_rom(&s->flash, OBJECT(dev_soc), "STM32F205.flash",
|
||||
FLASH_SIZE, &error_fatal);
|
||||
memory_region_init_alias(flash_alias, OBJECT(dev_soc),
|
||||
"STM32F205.flash.alias", flash, 0, FLASH_SIZE);
|
||||
memory_region_init_alias(&s->flash_alias, OBJECT(dev_soc),
|
||||
"STM32F205.flash.alias", &s->flash, 0, FLASH_SIZE);
|
||||
|
||||
memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, flash);
|
||||
memory_region_add_subregion(system_memory, 0, flash_alias);
|
||||
memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash);
|
||||
memory_region_add_subregion(system_memory, 0, &s->flash_alias);
|
||||
|
||||
memory_region_init_ram(sram, NULL, "STM32F205.sram", SRAM_SIZE,
|
||||
memory_region_init_ram(&s->sram, NULL, "STM32F205.sram", SRAM_SIZE,
|
||||
&error_fatal);
|
||||
memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram);
|
||||
memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, &s->sram);
|
||||
|
||||
armv7m = DEVICE(&s->armv7m);
|
||||
qdev_prop_set_uint32(armv7m, "num-irq", 96);
|
||||
qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type);
|
||||
qdev_prop_set_bit(armv7m, "enable-bitband", true);
|
||||
qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);
|
||||
qdev_connect_clock_in(armv7m, "refclk", s->refclk);
|
||||
object_property_set_link(OBJECT(&s->armv7m), "memory",
|
||||
OBJECT(get_system_memory()), &error_abort);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) {
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "exec/address-spaces.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/arm/stm32f405_soc.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
|
||||
#define SYSCFG_ADD 0x40013800
|
||||
|
@ -80,6 +81,9 @@ static void stm32f405_soc_initfn(Object *obj)
|
|||
}
|
||||
|
||||
object_initialize_child(obj, "exti", &s->exti, TYPE_STM32F4XX_EXTI);
|
||||
|
||||
s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
|
||||
s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
|
@ -91,6 +95,30 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp)
|
|||
Error *err = NULL;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* We use s->refclk internally and only define it with qdev_init_clock_in()
|
||||
* so it is correctly parented and not leaked on an init/deinit; it is not
|
||||
* intended as an externally exposed clock.
|
||||
*/
|
||||
if (clock_has_source(s->refclk)) {
|
||||
error_setg(errp, "refclk clock must not be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!clock_has_source(s->sysclk)) {
|
||||
error_setg(errp, "sysclk clock must be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: ideally we should model the SoC RCC and its ability to
|
||||
* change the sysclk frequency and define different sysclk sources.
|
||||
*/
|
||||
|
||||
/* The refclk always runs at frequency HCLK / 8 */
|
||||
clock_set_mul_div(s->refclk, 8, 1);
|
||||
clock_set_source(s->refclk, s->sysclk);
|
||||
|
||||
memory_region_init_rom(&s->flash, OBJECT(dev_soc), "STM32F405.flash",
|
||||
FLASH_SIZE, &err);
|
||||
if (err != NULL) {
|
||||
|
@ -116,6 +144,8 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp)
|
|||
qdev_prop_set_uint32(armv7m, "num-irq", 96);
|
||||
qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type);
|
||||
qdev_prop_set_bit(armv7m, "enable-bitband", true);
|
||||
qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);
|
||||
qdev_connect_clock_in(armv7m, "refclk", s->refclk);
|
||||
object_property_set_link(OBJECT(&s->armv7m), "memory",
|
||||
OBJECT(system_memory), &error_abort);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) {
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "qapi/error.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/arm/stm32f100_soc.h"
|
||||
#include "hw/arm/boot.h"
|
||||
|
@ -39,16 +40,15 @@
|
|||
static void stm32vldiscovery_init(MachineState *machine)
|
||||
{
|
||||
DeviceState *dev;
|
||||
Clock *sysclk;
|
||||
|
||||
/*
|
||||
* TODO: ideally we would model the SoC RCC and let it handle
|
||||
* system_clock_scale, including its ability to define different
|
||||
* possible SYSCLK sources.
|
||||
*/
|
||||
system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ;
|
||||
/* This clock doesn't need migration because it is fixed-frequency */
|
||||
sysclk = clock_new(OBJECT(machine), "SYSCLK");
|
||||
clock_set_hz(sysclk, SYSCLK_FRQ);
|
||||
|
||||
dev = qdev_new(TYPE_STM32F100_SOC);
|
||||
qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3"));
|
||||
qdev_connect_clock_in(dev, "sysclk", sysclk);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
|
||||
armv7m_load_kernel(ARM_CPU(first_cpu),
|
||||
|
@ -63,4 +63,3 @@ static void stm32vldiscovery_machine_init(MachineClass *mc)
|
|||
}
|
||||
|
||||
DEFINE_MACHINE("stm32vldiscovery", stm32vldiscovery_machine_init)
|
||||
|
||||
|
|
|
@ -200,6 +200,7 @@ static const char *valid_cpus[] = {
|
|||
ARM_CPU_TYPE_NAME("cortex-a53"),
|
||||
ARM_CPU_TYPE_NAME("cortex-a57"),
|
||||
ARM_CPU_TYPE_NAME("cortex-a72"),
|
||||
ARM_CPU_TYPE_NAME("a64fx"),
|
||||
ARM_CPU_TYPE_NAME("host"),
|
||||
ARM_CPU_TYPE_NAME("max"),
|
||||
};
|
||||
|
@ -2783,10 +2784,17 @@ static void machvirt_machine_init(void)
|
|||
}
|
||||
type_init(machvirt_machine_init);
|
||||
|
||||
static void virt_machine_6_1_options(MachineClass *mc)
|
||||
static void virt_machine_6_2_options(MachineClass *mc)
|
||||
{
|
||||
}
|
||||
DEFINE_VIRT_MACHINE_AS_LATEST(6, 1)
|
||||
DEFINE_VIRT_MACHINE_AS_LATEST(6, 2)
|
||||
|
||||
static void virt_machine_6_1_options(MachineClass *mc)
|
||||
{
|
||||
virt_machine_6_2_options(mc);
|
||||
compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
|
||||
}
|
||||
DEFINE_VIRT_MACHINE(6, 1)
|
||||
|
||||
static void virt_machine_6_0_options(MachineClass *mc)
|
||||
{
|
||||
|
|
|
@ -14,12 +14,50 @@
|
|||
#include "migration/vmstate.h"
|
||||
#include "hw/clock.h"
|
||||
|
||||
static bool muldiv_needed(void *opaque)
|
||||
{
|
||||
Clock *clk = opaque;
|
||||
|
||||
return clk->multiplier != 1 || clk->divider != 1;
|
||||
}
|
||||
|
||||
static int clock_pre_load(void *opaque)
|
||||
{
|
||||
Clock *clk = opaque;
|
||||
/*
|
||||
* The initial out-of-reset settings of the Clock might have been
|
||||
* configured by the device to be different from what we set
|
||||
* in clock_initfn(), so we must here set the default values to
|
||||
* be used if they are not in the inbound migration state.
|
||||
*/
|
||||
clk->multiplier = 1;
|
||||
clk->divider = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const VMStateDescription vmstate_muldiv = {
|
||||
.name = "clock/muldiv",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = muldiv_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(multiplier, Clock),
|
||||
VMSTATE_UINT32(divider, Clock),
|
||||
},
|
||||
};
|
||||
|
||||
const VMStateDescription vmstate_clock = {
|
||||
.name = "clock",
|
||||
.version_id = 0,
|
||||
.minimum_version_id = 0,
|
||||
.pre_load = clock_pre_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT64(period, Clock),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
},
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_muldiv,
|
||||
NULL
|
||||
},
|
||||
};
|
||||
|
|
|
@ -64,6 +64,15 @@ bool clock_set(Clock *clk, uint64_t period)
|
|||
return true;
|
||||
}
|
||||
|
||||
static uint64_t clock_get_child_period(Clock *clk)
|
||||
{
|
||||
/*
|
||||
* Return the period to be used for child clocks, which is the parent
|
||||
* clock period adjusted for for multiplier and divider effects.
|
||||
*/
|
||||
return muldiv64(clk->period, clk->multiplier, clk->divider);
|
||||
}
|
||||
|
||||
static void clock_call_callback(Clock *clk, ClockEvent event)
|
||||
{
|
||||
/*
|
||||
|
@ -78,15 +87,16 @@ static void clock_call_callback(Clock *clk, ClockEvent event)
|
|||
static void clock_propagate_period(Clock *clk, bool call_callbacks)
|
||||
{
|
||||
Clock *child;
|
||||
uint64_t child_period = clock_get_child_period(clk);
|
||||
|
||||
QLIST_FOREACH(child, &clk->children, sibling) {
|
||||
if (child->period != clk->period) {
|
||||
if (child->period != child_period) {
|
||||
if (call_callbacks) {
|
||||
clock_call_callback(child, ClockPreUpdate);
|
||||
}
|
||||
child->period = clk->period;
|
||||
child->period = child_period;
|
||||
trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk),
|
||||
CLOCK_PERIOD_TO_HZ(clk->period),
|
||||
CLOCK_PERIOD_TO_HZ(child->period),
|
||||
call_callbacks);
|
||||
if (call_callbacks) {
|
||||
clock_call_callback(child, ClockUpdate);
|
||||
|
@ -110,7 +120,7 @@ void clock_set_source(Clock *clk, Clock *src)
|
|||
|
||||
trace_clock_set_source(CLOCK_PATH(clk), CLOCK_PATH(src));
|
||||
|
||||
clk->period = src->period;
|
||||
clk->period = clock_get_child_period(src);
|
||||
QLIST_INSERT_HEAD(&src->children, clk, sibling);
|
||||
clk->source = src;
|
||||
clock_propagate_period(clk, false);
|
||||
|
@ -133,10 +143,23 @@ char *clock_display_freq(Clock *clk)
|
|||
return freq_to_str(clock_get_hz(clk));
|
||||
}
|
||||
|
||||
void clock_set_mul_div(Clock *clk, uint32_t multiplier, uint32_t divider)
|
||||
{
|
||||
assert(divider != 0);
|
||||
|
||||
trace_clock_set_mul_div(CLOCK_PATH(clk), clk->multiplier, multiplier,
|
||||
clk->divider, divider);
|
||||
clk->multiplier = multiplier;
|
||||
clk->divider = divider;
|
||||
}
|
||||
|
||||
static void clock_initfn(Object *obj)
|
||||
{
|
||||
Clock *clk = CLOCK(obj);
|
||||
|
||||
clk->multiplier = 1;
|
||||
clk->divider = 1;
|
||||
|
||||
QLIST_INIT(&clk->children);
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,9 @@
|
|||
#include "hw/virtio/virtio.h"
|
||||
#include "hw/virtio/virtio-pci.h"
|
||||
|
||||
GlobalProperty hw_compat_6_1[] = {};
|
||||
const size_t hw_compat_6_1_len = G_N_ELEMENTS(hw_compat_6_1);
|
||||
|
||||
GlobalProperty hw_compat_6_0[] = {
|
||||
{ "gpex-pcihost", "allow-unmapped-accesses", "false" },
|
||||
{ "i8042", "extended-state", "false"},
|
||||
|
|
|
@ -34,3 +34,4 @@ clock_disconnect(const char *clk) "'%s'"
|
|||
clock_set(const char *clk, uint64_t old, uint64_t new) "'%s', %"PRIu64"Hz->%"PRIu64"Hz"
|
||||
clock_propagate(const char *clk) "'%s'"
|
||||
clock_update(const char *clk, const char *src, uint64_t hz, int cb) "'%s', src='%s', val=%"PRIu64"Hz cb=%d"
|
||||
clock_set_mul_div(const char *clk, uint32_t oldmul, uint32_t mul, uint32_t olddiv, uint32_t div) "'%s', mul: %u -> %u, div: %u -> %u"
|
||||
|
|
|
@ -93,6 +93,9 @@
|
|||
#include "trace.h"
|
||||
#include CONFIG_DEVICES
|
||||
|
||||
GlobalProperty pc_compat_6_1[] = {};
|
||||
const size_t pc_compat_6_1_len = G_N_ELEMENTS(pc_compat_6_1);
|
||||
|
||||
GlobalProperty pc_compat_6_0[] = {
|
||||
{ "qemu64" "-" TYPE_X86_CPU, "family", "6" },
|
||||
{ "qemu64" "-" TYPE_X86_CPU, "model", "6" },
|
||||
|
|
|
@ -412,7 +412,7 @@ static void pc_i440fx_machine_options(MachineClass *m)
|
|||
machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE);
|
||||
}
|
||||
|
||||
static void pc_i440fx_6_1_machine_options(MachineClass *m)
|
||||
static void pc_i440fx_6_2_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_i440fx_machine_options(m);
|
||||
|
@ -421,6 +421,18 @@ static void pc_i440fx_6_1_machine_options(MachineClass *m)
|
|||
pcmc->default_cpu_version = 1;
|
||||
}
|
||||
|
||||
DEFINE_I440FX_MACHINE(v6_2, "pc-i440fx-6.2", NULL,
|
||||
pc_i440fx_6_2_machine_options);
|
||||
|
||||
static void pc_i440fx_6_1_machine_options(MachineClass *m)
|
||||
{
|
||||
pc_i440fx_6_2_machine_options(m);
|
||||
m->alias = NULL;
|
||||
m->is_default = false;
|
||||
compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len);
|
||||
compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len);
|
||||
}
|
||||
|
||||
DEFINE_I440FX_MACHINE(v6_1, "pc-i440fx-6.1", NULL,
|
||||
pc_i440fx_6_1_machine_options);
|
||||
|
||||
|
|
|
@ -354,7 +354,7 @@ static void pc_q35_machine_options(MachineClass *m)
|
|||
m->max_cpus = 288;
|
||||
}
|
||||
|
||||
static void pc_q35_6_1_machine_options(MachineClass *m)
|
||||
static void pc_q35_6_2_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_q35_machine_options(m);
|
||||
|
@ -362,6 +362,17 @@ static void pc_q35_6_1_machine_options(MachineClass *m)
|
|||
pcmc->default_cpu_version = 1;
|
||||
}
|
||||
|
||||
DEFINE_Q35_MACHINE(v6_2, "pc-q35-6.2", NULL,
|
||||
pc_q35_6_2_machine_options);
|
||||
|
||||
static void pc_q35_6_1_machine_options(MachineClass *m)
|
||||
{
|
||||
pc_q35_6_2_machine_options(m);
|
||||
m->alias = NULL;
|
||||
compat_props_add(m->compat_props, hw_compat_6_1, hw_compat_6_1_len);
|
||||
compat_props_add(m->compat_props, pc_compat_6_1, pc_compat_6_1_len);
|
||||
}
|
||||
|
||||
DEFINE_Q35_MACHINE(v6_1, "pc-q35-6.1", NULL,
|
||||
pc_q35_6_1_machine_options);
|
||||
|
||||
|
|
|
@ -262,8 +262,21 @@ static void gicd_write_irouter(GICv3State *s, MemTxAttrs attrs, int irq,
|
|||
gicv3_update(s, irq, 1);
|
||||
}
|
||||
|
||||
static MemTxResult gicd_readb(GICv3State *s, hwaddr offset,
|
||||
uint64_t *data, MemTxAttrs attrs)
|
||||
/**
|
||||
* gicd_readb
|
||||
* gicd_readw
|
||||
* gicd_readl
|
||||
* gicd_readq
|
||||
* gicd_writeb
|
||||
* gicd_writew
|
||||
* gicd_writel
|
||||
* gicd_writeq
|
||||
*
|
||||
* Return %true if the operation succeeded, %false otherwise.
|
||||
*/
|
||||
|
||||
static bool gicd_readb(GICv3State *s, hwaddr offset,
|
||||
uint64_t *data, MemTxAttrs attrs)
|
||||
{
|
||||
/* Most GICv3 distributor registers do not support byte accesses. */
|
||||
switch (offset) {
|
||||
|
@ -273,17 +286,17 @@ static MemTxResult gicd_readb(GICv3State *s, hwaddr offset,
|
|||
/* This GIC implementation always has affinity routing enabled,
|
||||
* so these registers are all RAZ/WI.
|
||||
*/
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_IPRIORITYR ... GICD_IPRIORITYR + 0x3ff:
|
||||
*data = gicd_read_ipriorityr(s, attrs, offset - GICD_IPRIORITYR);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
default:
|
||||
return MEMTX_ERROR;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static MemTxResult gicd_writeb(GICv3State *s, hwaddr offset,
|
||||
uint64_t value, MemTxAttrs attrs)
|
||||
static bool gicd_writeb(GICv3State *s, hwaddr offset,
|
||||
uint64_t value, MemTxAttrs attrs)
|
||||
{
|
||||
/* Most GICv3 distributor registers do not support byte accesses. */
|
||||
switch (offset) {
|
||||
|
@ -293,25 +306,25 @@ static MemTxResult gicd_writeb(GICv3State *s, hwaddr offset,
|
|||
/* This GIC implementation always has affinity routing enabled,
|
||||
* so these registers are all RAZ/WI.
|
||||
*/
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_IPRIORITYR ... GICD_IPRIORITYR + 0x3ff:
|
||||
{
|
||||
int irq = offset - GICD_IPRIORITYR;
|
||||
|
||||
if (irq < GIC_INTERNAL || irq >= s->num_irq) {
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
gicd_write_ipriorityr(s, attrs, irq, value);
|
||||
gicv3_update(s, irq, 1);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return MEMTX_ERROR;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static MemTxResult gicd_readw(GICv3State *s, hwaddr offset,
|
||||
uint64_t *data, MemTxAttrs attrs)
|
||||
static bool gicd_readw(GICv3State *s, hwaddr offset,
|
||||
uint64_t *data, MemTxAttrs attrs)
|
||||
{
|
||||
/* Only GICD_SETSPI_NSR, GICD_CLRSPI_NSR, GICD_SETSPI_SR and GICD_SETSPI_NSR
|
||||
* support 16 bit accesses, and those registers are all part of the
|
||||
|
@ -319,11 +332,11 @@ static MemTxResult gicd_readw(GICv3State *s, hwaddr offset,
|
|||
* implement (ie for us GICD_TYPER.MBIS == 0), so for us they are
|
||||
* reserved.
|
||||
*/
|
||||
return MEMTX_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
static MemTxResult gicd_writew(GICv3State *s, hwaddr offset,
|
||||
uint64_t value, MemTxAttrs attrs)
|
||||
static bool gicd_writew(GICv3State *s, hwaddr offset,
|
||||
uint64_t value, MemTxAttrs attrs)
|
||||
{
|
||||
/* Only GICD_SETSPI_NSR, GICD_CLRSPI_NSR, GICD_SETSPI_SR and GICD_SETSPI_NSR
|
||||
* support 16 bit accesses, and those registers are all part of the
|
||||
|
@ -331,11 +344,11 @@ static MemTxResult gicd_writew(GICv3State *s, hwaddr offset,
|
|||
* implement (ie for us GICD_TYPER.MBIS == 0), so for us they are
|
||||
* reserved.
|
||||
*/
|
||||
return MEMTX_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
static MemTxResult gicd_readl(GICv3State *s, hwaddr offset,
|
||||
uint64_t *data, MemTxAttrs attrs)
|
||||
static bool gicd_readl(GICv3State *s, hwaddr offset,
|
||||
uint64_t *data, MemTxAttrs attrs)
|
||||
{
|
||||
/* Almost all GICv3 distributor registers are 32-bit.
|
||||
* Note that WO registers must return an UNKNOWN value on reads,
|
||||
|
@ -363,7 +376,7 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset,
|
|||
} else {
|
||||
*data = s->gicd_ctlr;
|
||||
}
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_TYPER:
|
||||
{
|
||||
/* For this implementation:
|
||||
|
@ -387,61 +400,61 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset,
|
|||
|
||||
*data = (1 << 25) | (1 << 24) | (sec_extn << 10) |
|
||||
(0xf << 19) | itlinesnumber;
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
case GICD_IIDR:
|
||||
/* We claim to be an ARM r0p0 with a zero ProductID.
|
||||
* This is the same as an r0p0 GIC-500.
|
||||
*/
|
||||
*data = gicv3_iidr();
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_STATUSR:
|
||||
/* RAZ/WI for us (this is an optional register and our implementation
|
||||
* does not track RO/WO/reserved violations to report them to the guest)
|
||||
*/
|
||||
*data = 0;
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_IGROUPR ... GICD_IGROUPR + 0x7f:
|
||||
{
|
||||
int irq;
|
||||
|
||||
if (!attrs.secure && !(s->gicd_ctlr & GICD_CTLR_DS)) {
|
||||
*data = 0;
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
/* RAZ/WI for SGIs, PPIs, unimplemented irqs */
|
||||
irq = (offset - GICD_IGROUPR) * 8;
|
||||
if (irq < GIC_INTERNAL || irq >= s->num_irq) {
|
||||
*data = 0;
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
*data = *gic_bmp_ptr32(s->group, irq);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
case GICD_ISENABLER ... GICD_ISENABLER + 0x7f:
|
||||
*data = gicd_read_bitmap_reg(s, attrs, s->enabled, NULL,
|
||||
offset - GICD_ISENABLER);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_ICENABLER ... GICD_ICENABLER + 0x7f:
|
||||
*data = gicd_read_bitmap_reg(s, attrs, s->enabled, NULL,
|
||||
offset - GICD_ICENABLER);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_ISPENDR ... GICD_ISPENDR + 0x7f:
|
||||
*data = gicd_read_bitmap_reg(s, attrs, s->pending, mask_nsacr_ge1,
|
||||
offset - GICD_ISPENDR);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_ICPENDR ... GICD_ICPENDR + 0x7f:
|
||||
*data = gicd_read_bitmap_reg(s, attrs, s->pending, mask_nsacr_ge2,
|
||||
offset - GICD_ICPENDR);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_ISACTIVER ... GICD_ISACTIVER + 0x7f:
|
||||
*data = gicd_read_bitmap_reg(s, attrs, s->active, mask_nsacr_ge2,
|
||||
offset - GICD_ISACTIVER);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_ICACTIVER ... GICD_ICACTIVER + 0x7f:
|
||||
*data = gicd_read_bitmap_reg(s, attrs, s->active, mask_nsacr_ge2,
|
||||
offset - GICD_ICACTIVER);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_IPRIORITYR ... GICD_IPRIORITYR + 0x3ff:
|
||||
{
|
||||
int i, irq = offset - GICD_IPRIORITYR;
|
||||
|
@ -452,12 +465,12 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset,
|
|||
value |= gicd_read_ipriorityr(s, attrs, i);
|
||||
}
|
||||
*data = value;
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
case GICD_ITARGETSR ... GICD_ITARGETSR + 0x3ff:
|
||||
/* RAZ/WI since affinity routing is always enabled */
|
||||
*data = 0;
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_ICFGR ... GICD_ICFGR + 0xff:
|
||||
{
|
||||
/* Here only the even bits are used; odd bits are RES0 */
|
||||
|
@ -466,7 +479,7 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset,
|
|||
|
||||
if (irq < GIC_INTERNAL || irq >= s->num_irq) {
|
||||
*data = 0;
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Since our edge_trigger bitmap is one bit per irq, we only need
|
||||
|
@ -478,7 +491,7 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset,
|
|||
value = extract32(value, (irq & 0x1f) ? 16 : 0, 16);
|
||||
value = half_shuffle32(value) << 1;
|
||||
*data = value;
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
case GICD_IGRPMODR ... GICD_IGRPMODR + 0xff:
|
||||
{
|
||||
|
@ -489,16 +502,16 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset,
|
|||
* security enabled and this is an NS access
|
||||
*/
|
||||
*data = 0;
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
/* RAZ/WI for SGIs, PPIs, unimplemented irqs */
|
||||
irq = (offset - GICD_IGRPMODR) * 8;
|
||||
if (irq < GIC_INTERNAL || irq >= s->num_irq) {
|
||||
*data = 0;
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
*data = *gic_bmp_ptr32(s->grpmod, irq);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
case GICD_NSACR ... GICD_NSACR + 0xff:
|
||||
{
|
||||
|
@ -507,7 +520,7 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset,
|
|||
|
||||
if (irq < GIC_INTERNAL || irq >= s->num_irq) {
|
||||
*data = 0;
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((s->gicd_ctlr & GICD_CTLR_DS) || !attrs.secure) {
|
||||
|
@ -515,17 +528,17 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset,
|
|||
* security enabled and this is an NS access
|
||||
*/
|
||||
*data = 0;
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
*data = s->gicd_nsacr[irq / 16];
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
case GICD_CPENDSGIR ... GICD_CPENDSGIR + 0xf:
|
||||
case GICD_SPENDSGIR ... GICD_SPENDSGIR + 0xf:
|
||||
/* RAZ/WI since affinity routing is always enabled */
|
||||
*data = 0;
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_IROUTER ... GICD_IROUTER + 0x1fdf:
|
||||
{
|
||||
uint64_t r;
|
||||
|
@ -537,26 +550,26 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset,
|
|||
} else {
|
||||
*data = (uint32_t)r;
|
||||
}
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
case GICD_IDREGS ... GICD_IDREGS + 0x2f:
|
||||
/* ID registers */
|
||||
*data = gicv3_idreg(offset - GICD_IDREGS);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_SGIR:
|
||||
/* WO registers, return unknown value */
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid guest read from WO register at offset "
|
||||
TARGET_FMT_plx "\n", __func__, offset);
|
||||
*data = 0;
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
default:
|
||||
return MEMTX_ERROR;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static MemTxResult gicd_writel(GICv3State *s, hwaddr offset,
|
||||
uint64_t value, MemTxAttrs attrs)
|
||||
static bool gicd_writel(GICv3State *s, hwaddr offset,
|
||||
uint64_t value, MemTxAttrs attrs)
|
||||
{
|
||||
/* Almost all GICv3 distributor registers are 32-bit. Note that
|
||||
* RO registers must ignore writes, not abort.
|
||||
|
@ -600,68 +613,68 @@ static MemTxResult gicd_writel(GICv3State *s, hwaddr offset,
|
|||
s->gicd_ctlr &= ~(GICD_CTLR_EN_GRP1S | GICD_CTLR_ARE_NS);
|
||||
}
|
||||
gicv3_full_update(s);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
case GICD_STATUSR:
|
||||
/* RAZ/WI for our implementation */
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_IGROUPR ... GICD_IGROUPR + 0x7f:
|
||||
{
|
||||
int irq;
|
||||
|
||||
if (!attrs.secure && !(s->gicd_ctlr & GICD_CTLR_DS)) {
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
/* RAZ/WI for SGIs, PPIs, unimplemented irqs */
|
||||
irq = (offset - GICD_IGROUPR) * 8;
|
||||
if (irq < GIC_INTERNAL || irq >= s->num_irq) {
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
*gic_bmp_ptr32(s->group, irq) = value;
|
||||
gicv3_update(s, irq, 32);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
case GICD_ISENABLER ... GICD_ISENABLER + 0x7f:
|
||||
gicd_write_set_bitmap_reg(s, attrs, s->enabled, NULL,
|
||||
offset - GICD_ISENABLER, value);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_ICENABLER ... GICD_ICENABLER + 0x7f:
|
||||
gicd_write_clear_bitmap_reg(s, attrs, s->enabled, NULL,
|
||||
offset - GICD_ICENABLER, value);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_ISPENDR ... GICD_ISPENDR + 0x7f:
|
||||
gicd_write_set_bitmap_reg(s, attrs, s->pending, mask_nsacr_ge1,
|
||||
offset - GICD_ISPENDR, value);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_ICPENDR ... GICD_ICPENDR + 0x7f:
|
||||
gicd_write_clear_bitmap_reg(s, attrs, s->pending, mask_nsacr_ge2,
|
||||
offset - GICD_ICPENDR, value);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_ISACTIVER ... GICD_ISACTIVER + 0x7f:
|
||||
gicd_write_set_bitmap_reg(s, attrs, s->active, NULL,
|
||||
offset - GICD_ISACTIVER, value);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_ICACTIVER ... GICD_ICACTIVER + 0x7f:
|
||||
gicd_write_clear_bitmap_reg(s, attrs, s->active, NULL,
|
||||
offset - GICD_ICACTIVER, value);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_IPRIORITYR ... GICD_IPRIORITYR + 0x3ff:
|
||||
{
|
||||
int i, irq = offset - GICD_IPRIORITYR;
|
||||
|
||||
if (irq < GIC_INTERNAL || irq + 3 >= s->num_irq) {
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = irq; i < irq + 4; i++, value >>= 8) {
|
||||
gicd_write_ipriorityr(s, attrs, i, value);
|
||||
}
|
||||
gicv3_update(s, irq, 4);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
case GICD_ITARGETSR ... GICD_ITARGETSR + 0x3ff:
|
||||
/* RAZ/WI since affinity routing is always enabled */
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_ICFGR ... GICD_ICFGR + 0xff:
|
||||
{
|
||||
/* Here only the odd bits are used; even bits are RES0 */
|
||||
|
@ -669,7 +682,7 @@ static MemTxResult gicd_writel(GICv3State *s, hwaddr offset,
|
|||
uint32_t mask, oldval;
|
||||
|
||||
if (irq < GIC_INTERNAL || irq >= s->num_irq) {
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Since our edge_trigger bitmap is one bit per irq, our input
|
||||
|
@ -687,7 +700,7 @@ static MemTxResult gicd_writel(GICv3State *s, hwaddr offset,
|
|||
oldval = *gic_bmp_ptr32(s->edge_trigger, (irq & ~0x1f));
|
||||
value = (oldval & ~mask) | (value & mask);
|
||||
*gic_bmp_ptr32(s->edge_trigger, irq & ~0x1f) = value;
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
case GICD_IGRPMODR ... GICD_IGRPMODR + 0xff:
|
||||
{
|
||||
|
@ -697,16 +710,16 @@ static MemTxResult gicd_writel(GICv3State *s, hwaddr offset,
|
|||
/* RAZ/WI if security disabled, or if
|
||||
* security enabled and this is an NS access
|
||||
*/
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
/* RAZ/WI for SGIs, PPIs, unimplemented irqs */
|
||||
irq = (offset - GICD_IGRPMODR) * 8;
|
||||
if (irq < GIC_INTERNAL || irq >= s->num_irq) {
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
*gic_bmp_ptr32(s->grpmod, irq) = value;
|
||||
gicv3_update(s, irq, 32);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
case GICD_NSACR ... GICD_NSACR + 0xff:
|
||||
{
|
||||
|
@ -714,41 +727,41 @@ static MemTxResult gicd_writel(GICv3State *s, hwaddr offset,
|
|||
int irq = (offset - GICD_NSACR) * 4;
|
||||
|
||||
if (irq < GIC_INTERNAL || irq >= s->num_irq) {
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((s->gicd_ctlr & GICD_CTLR_DS) || !attrs.secure) {
|
||||
/* RAZ/WI if security disabled, or if
|
||||
* security enabled and this is an NS access
|
||||
*/
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
s->gicd_nsacr[irq / 16] = value;
|
||||
/* No update required as this only affects access permission checks */
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
case GICD_SGIR:
|
||||
/* RES0 if affinity routing is enabled */
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_CPENDSGIR ... GICD_CPENDSGIR + 0xf:
|
||||
case GICD_SPENDSGIR ... GICD_SPENDSGIR + 0xf:
|
||||
/* RAZ/WI since affinity routing is always enabled */
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
case GICD_IROUTER ... GICD_IROUTER + 0x1fdf:
|
||||
{
|
||||
uint64_t r;
|
||||
int irq = (offset - GICD_IROUTER) / 8;
|
||||
|
||||
if (irq < GIC_INTERNAL || irq >= s->num_irq) {
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Write half of the 64-bit register */
|
||||
r = gicd_read_irouter(s, attrs, irq);
|
||||
r = deposit64(r, (offset & 7) ? 32 : 0, 32, value);
|
||||
gicd_write_irouter(s, attrs, irq, r);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
}
|
||||
case GICD_IDREGS ... GICD_IDREGS + 0x2f:
|
||||
case GICD_TYPER:
|
||||
|
@ -757,14 +770,14 @@ static MemTxResult gicd_writel(GICv3State *s, hwaddr offset,
|
|||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid guest write to RO register at offset "
|
||||
TARGET_FMT_plx "\n", __func__, offset);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
default:
|
||||
return MEMTX_ERROR;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static MemTxResult gicd_writell(GICv3State *s, hwaddr offset,
|
||||
uint64_t value, MemTxAttrs attrs)
|
||||
static bool gicd_writeq(GICv3State *s, hwaddr offset,
|
||||
uint64_t value, MemTxAttrs attrs)
|
||||
{
|
||||
/* Our only 64-bit registers are GICD_IROUTER<n> */
|
||||
int irq;
|
||||
|
@ -773,14 +786,14 @@ static MemTxResult gicd_writell(GICv3State *s, hwaddr offset,
|
|||
case GICD_IROUTER ... GICD_IROUTER + 0x1fdf:
|
||||
irq = (offset - GICD_IROUTER) / 8;
|
||||
gicd_write_irouter(s, attrs, irq, value);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
default:
|
||||
return MEMTX_ERROR;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static MemTxResult gicd_readll(GICv3State *s, hwaddr offset,
|
||||
uint64_t *data, MemTxAttrs attrs)
|
||||
static bool gicd_readq(GICv3State *s, hwaddr offset,
|
||||
uint64_t *data, MemTxAttrs attrs)
|
||||
{
|
||||
/* Our only 64-bit registers are GICD_IROUTER<n> */
|
||||
int irq;
|
||||
|
@ -789,9 +802,9 @@ static MemTxResult gicd_readll(GICv3State *s, hwaddr offset,
|
|||
case GICD_IROUTER ... GICD_IROUTER + 0x1fdf:
|
||||
irq = (offset - GICD_IROUTER) / 8;
|
||||
*data = gicd_read_irouter(s, attrs, irq);
|
||||
return MEMTX_OK;
|
||||
return true;
|
||||
default:
|
||||
return MEMTX_ERROR;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -799,7 +812,7 @@ MemTxResult gicv3_dist_read(void *opaque, hwaddr offset, uint64_t *data,
|
|||
unsigned size, MemTxAttrs attrs)
|
||||
{
|
||||
GICv3State *s = (GICv3State *)opaque;
|
||||
MemTxResult r;
|
||||
bool r;
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
|
@ -812,14 +825,14 @@ MemTxResult gicv3_dist_read(void *opaque, hwaddr offset, uint64_t *data,
|
|||
r = gicd_readl(s, offset, data, attrs);
|
||||
break;
|
||||
case 8:
|
||||
r = gicd_readll(s, offset, data, attrs);
|
||||
r = gicd_readq(s, offset, data, attrs);
|
||||
break;
|
||||
default:
|
||||
r = MEMTX_ERROR;
|
||||
r = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (r == MEMTX_ERROR) {
|
||||
if (!r) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid guest read at offset " TARGET_FMT_plx
|
||||
"size %u\n", __func__, offset, size);
|
||||
|
@ -829,19 +842,18 @@ MemTxResult gicv3_dist_read(void *opaque, hwaddr offset, uint64_t *data,
|
|||
* trigger the guest-error logging but don't return it to
|
||||
* the caller, or we'll cause a spurious guest data abort.
|
||||
*/
|
||||
r = MEMTX_OK;
|
||||
*data = 0;
|
||||
} else {
|
||||
trace_gicv3_dist_read(offset, *data, size, attrs.secure);
|
||||
}
|
||||
return r;
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
MemTxResult gicv3_dist_write(void *opaque, hwaddr offset, uint64_t data,
|
||||
unsigned size, MemTxAttrs attrs)
|
||||
{
|
||||
GICv3State *s = (GICv3State *)opaque;
|
||||
MemTxResult r;
|
||||
bool r;
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
|
@ -854,14 +866,14 @@ MemTxResult gicv3_dist_write(void *opaque, hwaddr offset, uint64_t data,
|
|||
r = gicd_writel(s, offset, data, attrs);
|
||||
break;
|
||||
case 8:
|
||||
r = gicd_writell(s, offset, data, attrs);
|
||||
r = gicd_writeq(s, offset, data, attrs);
|
||||
break;
|
||||
default:
|
||||
r = MEMTX_ERROR;
|
||||
r = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (r == MEMTX_ERROR) {
|
||||
if (!r) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid guest write at offset " TARGET_FMT_plx
|
||||
"size %u\n", __func__, offset, size);
|
||||
|
@ -871,11 +883,10 @@ MemTxResult gicv3_dist_write(void *opaque, hwaddr offset, uint64_t data,
|
|||
* trigger the guest-error logging but don't return it to
|
||||
* the caller, or we'll cause a spurious guest data abort.
|
||||
*/
|
||||
r = MEMTX_OK;
|
||||
} else {
|
||||
trace_gicv3_dist_write(offset, data, size, attrs.secure);
|
||||
}
|
||||
return r;
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
void gicv3_dist_set_irq(GICv3State *s, int irq, int level)
|
||||
|
|
|
@ -2470,172 +2470,6 @@ static const MemoryRegionOps nvic_sysreg_ops = {
|
|||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static MemTxResult nvic_sysreg_ns_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
MemoryRegion *mr = opaque;
|
||||
|
||||
if (attrs.secure) {
|
||||
/* S accesses to the alias act like NS accesses to the real region */
|
||||
attrs.secure = 0;
|
||||
return memory_region_dispatch_write(mr, addr, value,
|
||||
size_memop(size) | MO_TE, attrs);
|
||||
} else {
|
||||
/* NS attrs are RAZ/WI for privileged, and BusFault for user */
|
||||
if (attrs.user) {
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
return MEMTX_OK;
|
||||
}
|
||||
}
|
||||
|
||||
static MemTxResult nvic_sysreg_ns_read(void *opaque, hwaddr addr,
|
||||
uint64_t *data, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
MemoryRegion *mr = opaque;
|
||||
|
||||
if (attrs.secure) {
|
||||
/* S accesses to the alias act like NS accesses to the real region */
|
||||
attrs.secure = 0;
|
||||
return memory_region_dispatch_read(mr, addr, data,
|
||||
size_memop(size) | MO_TE, attrs);
|
||||
} else {
|
||||
/* NS attrs are RAZ/WI for privileged, and BusFault for user */
|
||||
if (attrs.user) {
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
*data = 0;
|
||||
return MEMTX_OK;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps nvic_sysreg_ns_ops = {
|
||||
.read_with_attrs = nvic_sysreg_ns_read,
|
||||
.write_with_attrs = nvic_sysreg_ns_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static MemTxResult nvic_systick_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
NVICState *s = opaque;
|
||||
MemoryRegion *mr;
|
||||
|
||||
/* Direct the access to the correct systick */
|
||||
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systick[attrs.secure]), 0);
|
||||
return memory_region_dispatch_write(mr, addr, value,
|
||||
size_memop(size) | MO_TE, attrs);
|
||||
}
|
||||
|
||||
static MemTxResult nvic_systick_read(void *opaque, hwaddr addr,
|
||||
uint64_t *data, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
NVICState *s = opaque;
|
||||
MemoryRegion *mr;
|
||||
|
||||
/* Direct the access to the correct systick */
|
||||
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systick[attrs.secure]), 0);
|
||||
return memory_region_dispatch_read(mr, addr, data, size_memop(size) | MO_TE,
|
||||
attrs);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps nvic_systick_ops = {
|
||||
.read_with_attrs = nvic_systick_read,
|
||||
.write_with_attrs = nvic_systick_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
|
||||
static MemTxResult ras_read(void *opaque, hwaddr addr,
|
||||
uint64_t *data, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
if (attrs.user) {
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case 0xe10: /* ERRIIDR */
|
||||
/* architect field = Arm; product/variant/revision 0 */
|
||||
*data = 0x43b;
|
||||
break;
|
||||
case 0xfc8: /* ERRDEVID */
|
||||
/* Minimal RAS: we implement 0 error record indexes */
|
||||
*data = 0;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "Read RAS register offset 0x%x\n",
|
||||
(uint32_t)addr);
|
||||
*data = 0;
|
||||
break;
|
||||
}
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
static MemTxResult ras_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
if (attrs.user) {
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "Write to RAS register offset 0x%x\n",
|
||||
(uint32_t)addr);
|
||||
break;
|
||||
}
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ras_ops = {
|
||||
.read_with_attrs = ras_read,
|
||||
.write_with_attrs = ras_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
/*
|
||||
* Unassigned portions of the PPB space are RAZ/WI for privileged
|
||||
* accesses, and fault for non-privileged accesses.
|
||||
*/
|
||||
static MemTxResult ppb_default_read(void *opaque, hwaddr addr,
|
||||
uint64_t *data, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
qemu_log_mask(LOG_UNIMP, "Read of unassigned area of PPB: offset 0x%x\n",
|
||||
(uint32_t)addr);
|
||||
if (attrs.user) {
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
*data = 0;
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
static MemTxResult ppb_default_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
qemu_log_mask(LOG_UNIMP, "Write of unassigned area of PPB: offset 0x%x\n",
|
||||
(uint32_t)addr);
|
||||
if (attrs.user) {
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ppb_default_ops = {
|
||||
.read_with_attrs = ppb_default_read,
|
||||
.write_with_attrs = ppb_default_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid.min_access_size = 1,
|
||||
.valid.max_access_size = 8,
|
||||
};
|
||||
|
||||
static int nvic_post_load(void *opaque, int version_id)
|
||||
{
|
||||
NVICState *s = opaque;
|
||||
|
@ -2851,108 +2685,14 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp)
|
|||
|
||||
s->num_prio_bits = arm_feature(&s->cpu->env, ARM_FEATURE_V7) ? 8 : 2;
|
||||
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->systick[M_REG_NS]), errp)) {
|
||||
return;
|
||||
}
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->systick[M_REG_NS]), 0,
|
||||
qdev_get_gpio_in_named(dev, "systick-trigger",
|
||||
M_REG_NS));
|
||||
|
||||
if (arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY)) {
|
||||
/* We couldn't init the secure systick device in instance_init
|
||||
* as we didn't know then if the CPU had the security extensions;
|
||||
* so we have to do it here.
|
||||
*/
|
||||
object_initialize_child(OBJECT(dev), "systick-reg-s",
|
||||
&s->systick[M_REG_S], TYPE_SYSTICK);
|
||||
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->systick[M_REG_S]), errp)) {
|
||||
return;
|
||||
}
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->systick[M_REG_S]), 0,
|
||||
qdev_get_gpio_in_named(dev, "systick-trigger",
|
||||
M_REG_S));
|
||||
}
|
||||
|
||||
/*
|
||||
* This device provides a single sysbus memory region which
|
||||
* represents the whole of the "System PPB" space. This is the
|
||||
* range from 0xe0000000 to 0xe00fffff and includes the NVIC,
|
||||
* the System Control Space (system registers), the systick timer,
|
||||
* and for CPUs with the Security extension an NS banked version
|
||||
* of all of these.
|
||||
*
|
||||
* The default behaviour for unimplemented registers/ranges
|
||||
* (for instance the Data Watchpoint and Trace unit at 0xe0001000)
|
||||
* is to RAZ/WI for privileged access and BusFault for non-privileged
|
||||
* access.
|
||||
*
|
||||
* The NVIC and System Control Space (SCS) starts at 0xe000e000
|
||||
* and looks like this:
|
||||
* 0x004 - ICTR
|
||||
* 0x010 - 0xff - systick
|
||||
* 0x100..0x7ec - NVIC
|
||||
* 0x7f0..0xcff - Reserved
|
||||
* 0xd00..0xd3c - SCS registers
|
||||
* 0xd40..0xeff - Reserved or Not implemented
|
||||
* 0xf00 - STIR
|
||||
*
|
||||
* Some registers within this space are banked between security states.
|
||||
* In v8M there is a second range 0xe002e000..0xe002efff which is the
|
||||
* NonSecure alias SCS; secure accesses to this behave like NS accesses
|
||||
* to the main SCS range, and non-secure accesses (including when
|
||||
* the security extension is not implemented) are RAZ/WI.
|
||||
* Note that both the main SCS range and the alias range are defined
|
||||
* to be exempt from memory attribution (R_BLJT) and so the memory
|
||||
* transaction attribute always matches the current CPU security
|
||||
* state (attrs.secure == env->v7m.secure). In the nvic_sysreg_ns_ops
|
||||
* wrappers we change attrs.secure to indicate the NS access; so
|
||||
* generally code determining which banked register to use should
|
||||
* use attrs.secure; code determining actual behaviour of the system
|
||||
* should use env->v7m.secure.
|
||||
*
|
||||
* The container covers the whole PPB space. Within it the priority
|
||||
* of overlapping regions is:
|
||||
* - default region (for RAZ/WI and BusFault) : -1
|
||||
* - system register regions : 0
|
||||
* - systick : 1
|
||||
* This is because the systick device is a small block of registers
|
||||
* in the middle of the other system control registers.
|
||||
* This device provides a single memory region which covers the
|
||||
* sysreg/NVIC registers from 0xE000E000 .. 0xE000EFFF, with the
|
||||
* exception of the systick timer registers 0xE000E010 .. 0xE000E0FF.
|
||||
*/
|
||||
memory_region_init(&s->container, OBJECT(s), "nvic", 0x100000);
|
||||
memory_region_init_io(&s->defaultmem, OBJECT(s), &ppb_default_ops, s,
|
||||
"nvic-default", 0x100000);
|
||||
memory_region_add_subregion_overlap(&s->container, 0, &s->defaultmem, -1);
|
||||
memory_region_init_io(&s->sysregmem, OBJECT(s), &nvic_sysreg_ops, s,
|
||||
"nvic_sysregs", 0x1000);
|
||||
memory_region_add_subregion(&s->container, 0xe000, &s->sysregmem);
|
||||
|
||||
memory_region_init_io(&s->systickmem, OBJECT(s),
|
||||
&nvic_systick_ops, s,
|
||||
"nvic_systick", 0xe0);
|
||||
|
||||
memory_region_add_subregion_overlap(&s->container, 0xe010,
|
||||
&s->systickmem, 1);
|
||||
|
||||
if (arm_feature(&s->cpu->env, ARM_FEATURE_V8)) {
|
||||
memory_region_init_io(&s->sysreg_ns_mem, OBJECT(s),
|
||||
&nvic_sysreg_ns_ops, &s->sysregmem,
|
||||
"nvic_sysregs_ns", 0x1000);
|
||||
memory_region_add_subregion(&s->container, 0x2e000, &s->sysreg_ns_mem);
|
||||
memory_region_init_io(&s->systick_ns_mem, OBJECT(s),
|
||||
&nvic_sysreg_ns_ops, &s->systickmem,
|
||||
"nvic_systick_ns", 0xe0);
|
||||
memory_region_add_subregion_overlap(&s->container, 0x2e010,
|
||||
&s->systick_ns_mem, 1);
|
||||
}
|
||||
|
||||
if (cpu_isar_feature(aa32_ras, s->cpu)) {
|
||||
memory_region_init_io(&s->ras_mem, OBJECT(s),
|
||||
&ras_ops, s, "nvic_ras", 0x1000);
|
||||
memory_region_add_subregion(&s->container, 0x5000, &s->ras_mem);
|
||||
}
|
||||
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->container);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->sysregmem);
|
||||
}
|
||||
|
||||
static void armv7m_nvic_instance_init(Object *obj)
|
||||
|
@ -2961,12 +2701,6 @@ static void armv7m_nvic_instance_init(Object *obj)
|
|||
NVICState *nvic = NVIC(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
object_initialize_child(obj, "systick-reg-ns", &nvic->systick[M_REG_NS],
|
||||
TYPE_SYSTICK);
|
||||
/* We can't initialize the secure systick here, as we don't know
|
||||
* yet if we need it.
|
||||
*/
|
||||
|
||||
sysbus_init_irq(sbd, &nvic->excpout);
|
||||
qdev_init_gpio_out_named(dev, &nvic->sysresetreq, "SYSRESETREQ", 1);
|
||||
qdev_init_gpio_in_named(dev, nvic_systick_trigger, "systick-trigger",
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Arm M-profile RAS (Reliability, Availability and Serviceability) block
|
||||
*
|
||||
* Copyright (c) 2021 Linaro Limited
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/misc/armv7m_ras.h"
|
||||
#include "qemu/log.h"
|
||||
|
||||
static MemTxResult ras_read(void *opaque, hwaddr addr,
|
||||
uint64_t *data, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
if (attrs.user) {
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case 0xe10: /* ERRIIDR */
|
||||
/* architect field = Arm; product/variant/revision 0 */
|
||||
*data = 0x43b;
|
||||
break;
|
||||
case 0xfc8: /* ERRDEVID */
|
||||
/* Minimal RAS: we implement 0 error record indexes */
|
||||
*data = 0;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "Read RAS register offset 0x%x\n",
|
||||
(uint32_t)addr);
|
||||
*data = 0;
|
||||
break;
|
||||
}
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
static MemTxResult ras_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
if (attrs.user) {
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "Write to RAS register offset 0x%x\n",
|
||||
(uint32_t)addr);
|
||||
break;
|
||||
}
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ras_ops = {
|
||||
.read_with_attrs = ras_read,
|
||||
.write_with_attrs = ras_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
|
||||
static void armv7m_ras_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
ARMv7MRAS *s = ARMV7M_RAS(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ras_ops,
|
||||
s, "armv7m-ras", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void armv7m_ras_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
/* This device has no state: no need for vmstate or reset */
|
||||
}
|
||||
|
||||
static const TypeInfo armv7m_ras_info = {
|
||||
.name = TYPE_ARMV7M_RAS,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(ARMv7MRAS),
|
||||
.instance_init = armv7m_ras_init,
|
||||
.class_init = armv7m_ras_class_init,
|
||||
};
|
||||
|
||||
static void armv7m_ras_register_types(void)
|
||||
{
|
||||
type_register_static(&armv7m_ras_info);
|
||||
}
|
||||
|
||||
type_init(armv7m_ras_register_types);
|
|
@ -17,6 +17,8 @@ softmmu_ss.add(when: 'CONFIG_INTEGRATOR_DEBUG', if_true: files('arm_integrator_d
|
|||
softmmu_ss.add(when: 'CONFIG_A9SCU', if_true: files('a9scu.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_ARM11SCU', if_true: files('arm11scu.c'))
|
||||
|
||||
softmmu_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_ras.c'))
|
||||
|
||||
# Mac devices
|
||||
softmmu_ss.add(when: 'CONFIG_MOS6522', if_true: files('mos6522.c'))
|
||||
|
||||
|
|
|
@ -4686,14 +4686,25 @@ static void spapr_machine_latest_class_options(MachineClass *mc)
|
|||
type_init(spapr_machine_register_##suffix)
|
||||
|
||||
/*
|
||||
* pseries-6.1
|
||||
* pseries-6.2
|
||||
*/
|
||||
static void spapr_machine_6_1_class_options(MachineClass *mc)
|
||||
static void spapr_machine_6_2_class_options(MachineClass *mc)
|
||||
{
|
||||
/* Defaults for the latest behaviour inherited from the base class */
|
||||
}
|
||||
|
||||
DEFINE_SPAPR_MACHINE(6_1, "6.1", true);
|
||||
DEFINE_SPAPR_MACHINE(6_2, "6.2", true);
|
||||
|
||||
/*
|
||||
* pseries-6.1
|
||||
*/
|
||||
static void spapr_machine_6_1_class_options(MachineClass *mc)
|
||||
{
|
||||
spapr_machine_6_2_class_options(mc);
|
||||
compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
|
||||
}
|
||||
|
||||
DEFINE_SPAPR_MACHINE(6_1, "6.1", false);
|
||||
|
||||
/*
|
||||
* pseries-6.0
|
||||
|
|
|
@ -791,14 +791,26 @@ bool css_migration_enabled(void)
|
|||
} \
|
||||
type_init(ccw_machine_register_##suffix)
|
||||
|
||||
static void ccw_machine_6_2_instance_options(MachineState *machine)
|
||||
{
|
||||
}
|
||||
|
||||
static void ccw_machine_6_2_class_options(MachineClass *mc)
|
||||
{
|
||||
}
|
||||
DEFINE_CCW_MACHINE(6_2, "6.2", true);
|
||||
|
||||
static void ccw_machine_6_1_instance_options(MachineState *machine)
|
||||
{
|
||||
ccw_machine_6_2_instance_options(machine);
|
||||
}
|
||||
|
||||
static void ccw_machine_6_1_class_options(MachineClass *mc)
|
||||
{
|
||||
ccw_machine_6_2_class_options(mc);
|
||||
compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
|
||||
}
|
||||
DEFINE_CCW_MACHINE(6_1, "6.1", true);
|
||||
DEFINE_CCW_MACHINE(6_1, "6.1", false);
|
||||
|
||||
static void ccw_machine_6_0_instance_options(MachineState *machine)
|
||||
{
|
||||
|
|
|
@ -52,5 +52,8 @@ config SSE_COUNTER
|
|||
config SSE_TIMER
|
||||
bool
|
||||
|
||||
config STELLARIS_GPTM
|
||||
bool
|
||||
|
||||
config AVR_TIMER16
|
||||
bool
|
||||
|
|
|
@ -14,28 +14,32 @@
|
|||
#include "migration/vmstate.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qapi/error.h"
|
||||
#include "trace.h"
|
||||
|
||||
/* qemu timers run at 1GHz. We want something closer to 1MHz. */
|
||||
#define SYSTICK_SCALE 1000ULL
|
||||
|
||||
#define SYSTICK_ENABLE (1 << 0)
|
||||
#define SYSTICK_TICKINT (1 << 1)
|
||||
#define SYSTICK_CLKSOURCE (1 << 2)
|
||||
#define SYSTICK_COUNTFLAG (1 << 16)
|
||||
|
||||
int system_clock_scale;
|
||||
#define SYSCALIB_NOREF (1U << 31)
|
||||
#define SYSCALIB_SKEW (1U << 30)
|
||||
#define SYSCALIB_TENMS ((1U << 24) - 1)
|
||||
|
||||
/* Conversion factor from qemu timer to SysTick frequencies. */
|
||||
static inline int64_t systick_scale(SysTickState *s)
|
||||
static void systick_set_period_from_clock(SysTickState *s)
|
||||
{
|
||||
/*
|
||||
* Set the ptimer period from whichever clock is selected.
|
||||
* Must be called from within a ptimer transaction block.
|
||||
*/
|
||||
if (s->control & SYSTICK_CLKSOURCE) {
|
||||
return system_clock_scale;
|
||||
ptimer_set_period_from_clock(s->ptimer, s->cpuclk, 1);
|
||||
} else {
|
||||
return 1000;
|
||||
ptimer_set_period_from_clock(s->ptimer, s->refclk, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,7 +86,28 @@ static MemTxResult systick_read(void *opaque, hwaddr addr, uint64_t *data,
|
|||
val = ptimer_get_count(s->ptimer);
|
||||
break;
|
||||
case 0xc: /* SysTick Calibration Value. */
|
||||
val = 10000;
|
||||
/*
|
||||
* In real hardware it is possible to make this register report
|
||||
* a different value from what the reference clock is actually
|
||||
* running at. We don't model that (which usually happens due
|
||||
* to integration errors in the real hardware) and instead always
|
||||
* report the theoretical correct value as described in the
|
||||
* knowledgebase article at
|
||||
* https://developer.arm.com/documentation/ka001325/latest
|
||||
* If necessary, we could implement an extra QOM property on this
|
||||
* device to force the STCALIB value to something different from
|
||||
* the "correct" value.
|
||||
*/
|
||||
if (!clock_has_source(s->refclk)) {
|
||||
val = SYSCALIB_NOREF;
|
||||
break;
|
||||
}
|
||||
val = clock_ns_to_ticks(s->refclk, 10 * SCALE_MS) - 1;
|
||||
val &= SYSCALIB_TENMS;
|
||||
if (clock_ticks_to_ns(s->refclk, val + 1) != 10 * SCALE_MS) {
|
||||
/* report that tick count does not yield exactly 10ms */
|
||||
val |= SYSCALIB_SKEW;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
val = 0;
|
||||
|
@ -114,6 +139,11 @@ static MemTxResult systick_write(void *opaque, hwaddr addr,
|
|||
{
|
||||
uint32_t oldval;
|
||||
|
||||
if (!clock_has_source(s->refclk)) {
|
||||
/* This bit is always 1 if there is no external refclk */
|
||||
value |= SYSTICK_CLKSOURCE;
|
||||
}
|
||||
|
||||
ptimer_transaction_begin(s->ptimer);
|
||||
oldval = s->control;
|
||||
s->control &= 0xfffffff8;
|
||||
|
@ -121,19 +151,14 @@ static MemTxResult systick_write(void *opaque, hwaddr addr,
|
|||
|
||||
if ((oldval ^ value) & SYSTICK_ENABLE) {
|
||||
if (value & SYSTICK_ENABLE) {
|
||||
/*
|
||||
* Always reload the period in case board code has
|
||||
* changed system_clock_scale. If we ever replace that
|
||||
* global with a more sensible API then we might be able
|
||||
* to set the period only when it actually changes.
|
||||
*/
|
||||
ptimer_set_period(s->ptimer, systick_scale(s));
|
||||
ptimer_run(s->ptimer, 0);
|
||||
} else {
|
||||
ptimer_stop(s->ptimer);
|
||||
}
|
||||
} else if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
|
||||
ptimer_set_period(s->ptimer, systick_scale(s));
|
||||
}
|
||||
|
||||
if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
|
||||
systick_set_period_from_clock(s);
|
||||
}
|
||||
ptimer_transaction_commit(s->ptimer);
|
||||
break;
|
||||
|
@ -176,20 +201,42 @@ static void systick_reset(DeviceState *dev)
|
|||
{
|
||||
SysTickState *s = SYSTICK(dev);
|
||||
|
||||
/*
|
||||
* Forgetting to set system_clock_scale is always a board code
|
||||
* bug. We can't check this earlier because for some boards
|
||||
* (like stellaris) it is not yet configured at the point where
|
||||
* the systick device is realized.
|
||||
*/
|
||||
assert(system_clock_scale != 0);
|
||||
|
||||
ptimer_transaction_begin(s->ptimer);
|
||||
s->control = 0;
|
||||
if (!clock_has_source(s->refclk)) {
|
||||
/* This bit is always 1 if there is no external refclk */
|
||||
s->control |= SYSTICK_CLKSOURCE;
|
||||
}
|
||||
ptimer_stop(s->ptimer);
|
||||
ptimer_set_count(s->ptimer, 0);
|
||||
ptimer_set_limit(s->ptimer, 0, 0);
|
||||
ptimer_set_period(s->ptimer, systick_scale(s));
|
||||
systick_set_period_from_clock(s);
|
||||
ptimer_transaction_commit(s->ptimer);
|
||||
}
|
||||
|
||||
static void systick_cpuclk_update(void *opaque, ClockEvent event)
|
||||
{
|
||||
SysTickState *s = SYSTICK(opaque);
|
||||
|
||||
if (!(s->control & SYSTICK_CLKSOURCE)) {
|
||||
/* currently using refclk, we can ignore cpuclk changes */
|
||||
}
|
||||
|
||||
ptimer_transaction_begin(s->ptimer);
|
||||
ptimer_set_period_from_clock(s->ptimer, s->cpuclk, 1);
|
||||
ptimer_transaction_commit(s->ptimer);
|
||||
}
|
||||
|
||||
static void systick_refclk_update(void *opaque, ClockEvent event)
|
||||
{
|
||||
SysTickState *s = SYSTICK(opaque);
|
||||
|
||||
if (s->control & SYSTICK_CLKSOURCE) {
|
||||
/* currently using cpuclk, we can ignore refclk changes */
|
||||
}
|
||||
|
||||
ptimer_transaction_begin(s->ptimer);
|
||||
ptimer_set_period_from_clock(s->ptimer, s->refclk, 1);
|
||||
ptimer_transaction_commit(s->ptimer);
|
||||
}
|
||||
|
||||
|
@ -201,6 +248,11 @@ static void systick_instance_init(Object *obj)
|
|||
memory_region_init_io(&s->iomem, obj, &systick_ops, s, "systick", 0xe0);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk",
|
||||
systick_refclk_update, s, ClockUpdate);
|
||||
s->cpuclk = qdev_init_clock_in(DEVICE(obj), "cpuclk",
|
||||
systick_cpuclk_update, s, ClockUpdate);
|
||||
}
|
||||
|
||||
static void systick_realize(DeviceState *dev, Error **errp)
|
||||
|
@ -211,13 +263,21 @@ static void systick_realize(DeviceState *dev, Error **errp)
|
|||
PTIMER_POLICY_NO_COUNTER_ROUND_DOWN |
|
||||
PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
|
||||
PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT);
|
||||
|
||||
if (!clock_has_source(s->cpuclk)) {
|
||||
error_setg(errp, "systick: cpuclk must be connected");
|
||||
return;
|
||||
}
|
||||
/* It's OK not to connect the refclk */
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_systick = {
|
||||
.name = "armv7m_systick",
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 2,
|
||||
.version_id = 3,
|
||||
.minimum_version_id = 3,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_CLOCK(refclk, SysTickState),
|
||||
VMSTATE_CLOCK(cpuclk, SysTickState),
|
||||
VMSTATE_UINT32(control, SysTickState),
|
||||
VMSTATE_INT64(tick, SysTickState),
|
||||
VMSTATE_PTIMER(ptimer, SysTickState),
|
||||
|
|
|
@ -31,6 +31,7 @@ softmmu_ss.add(when: 'CONFIG_SH_TIMER', if_true: files('sh_timer.c'))
|
|||
softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_timer.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_SSE_COUNTER', if_true: files('sse-counter.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_SSE_TIMER', if_true: files('sse-timer.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_STELLARIS_GPTM', if_true: files('stellaris-gptm.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_STM32F2XX_TIMER', if_true: files('stm32f2xx_timer.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_timer.c'))
|
||||
specific_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_timer.c'))
|
||||
|
|
|
@ -0,0 +1,332 @@
|
|||
/*
|
||||
* Luminary Micro Stellaris General Purpose Timer Module
|
||||
*
|
||||
* Copyright (c) 2006 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licensed under the GPL.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qapi/error.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "hw/timer/stellaris-gptm.h"
|
||||
|
||||
static void gptm_update_irq(gptm_state *s)
|
||||
{
|
||||
int level;
|
||||
level = (s->state & s->mask) != 0;
|
||||
qemu_set_irq(s->irq, level);
|
||||
}
|
||||
|
||||
static void gptm_stop(gptm_state *s, int n)
|
||||
{
|
||||
timer_del(s->timer[n]);
|
||||
}
|
||||
|
||||
static void gptm_reload(gptm_state *s, int n, int reset)
|
||||
{
|
||||
int64_t tick;
|
||||
if (reset) {
|
||||
tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
} else {
|
||||
tick = s->tick[n];
|
||||
}
|
||||
|
||||
if (s->config == 0) {
|
||||
/* 32-bit CountDown. */
|
||||
uint32_t count;
|
||||
count = s->load[0] | (s->load[1] << 16);
|
||||
tick += clock_ticks_to_ns(s->clk, count);
|
||||
} else if (s->config == 1) {
|
||||
/* 32-bit RTC. 1Hz tick. */
|
||||
tick += NANOSECONDS_PER_SECOND;
|
||||
} else if (s->mode[n] == 0xa) {
|
||||
/* PWM mode. Not implemented. */
|
||||
} else {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"GPTM: 16-bit timer mode unimplemented: 0x%x\n",
|
||||
s->mode[n]);
|
||||
return;
|
||||
}
|
||||
s->tick[n] = tick;
|
||||
timer_mod(s->timer[n], tick);
|
||||
}
|
||||
|
||||
static void gptm_tick(void *opaque)
|
||||
{
|
||||
gptm_state **p = (gptm_state **)opaque;
|
||||
gptm_state *s;
|
||||
int n;
|
||||
|
||||
s = *p;
|
||||
n = p - s->opaque;
|
||||
if (s->config == 0) {
|
||||
s->state |= 1;
|
||||
if ((s->control & 0x20)) {
|
||||
/* Output trigger. */
|
||||
qemu_irq_pulse(s->trigger);
|
||||
}
|
||||
if (s->mode[0] & 1) {
|
||||
/* One-shot. */
|
||||
s->control &= ~1;
|
||||
} else {
|
||||
/* Periodic. */
|
||||
gptm_reload(s, 0, 0);
|
||||
}
|
||||
} else if (s->config == 1) {
|
||||
/* RTC. */
|
||||
uint32_t match;
|
||||
s->rtc++;
|
||||
match = s->match[0] | (s->match[1] << 16);
|
||||
if (s->rtc > match)
|
||||
s->rtc = 0;
|
||||
if (s->rtc == 0) {
|
||||
s->state |= 8;
|
||||
}
|
||||
gptm_reload(s, 0, 0);
|
||||
} else if (s->mode[n] == 0xa) {
|
||||
/* PWM mode. Not implemented. */
|
||||
} else {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"GPTM: 16-bit timer mode unimplemented: 0x%x\n",
|
||||
s->mode[n]);
|
||||
}
|
||||
gptm_update_irq(s);
|
||||
}
|
||||
|
||||
static uint64_t gptm_read(void *opaque, hwaddr offset,
|
||||
unsigned size)
|
||||
{
|
||||
gptm_state *s = (gptm_state *)opaque;
|
||||
|
||||
switch (offset) {
|
||||
case 0x00: /* CFG */
|
||||
return s->config;
|
||||
case 0x04: /* TAMR */
|
||||
return s->mode[0];
|
||||
case 0x08: /* TBMR */
|
||||
return s->mode[1];
|
||||
case 0x0c: /* CTL */
|
||||
return s->control;
|
||||
case 0x18: /* IMR */
|
||||
return s->mask;
|
||||
case 0x1c: /* RIS */
|
||||
return s->state;
|
||||
case 0x20: /* MIS */
|
||||
return s->state & s->mask;
|
||||
case 0x24: /* CR */
|
||||
return 0;
|
||||
case 0x28: /* TAILR */
|
||||
return s->load[0] | ((s->config < 4) ? (s->load[1] << 16) : 0);
|
||||
case 0x2c: /* TBILR */
|
||||
return s->load[1];
|
||||
case 0x30: /* TAMARCHR */
|
||||
return s->match[0] | ((s->config < 4) ? (s->match[1] << 16) : 0);
|
||||
case 0x34: /* TBMATCHR */
|
||||
return s->match[1];
|
||||
case 0x38: /* TAPR */
|
||||
return s->prescale[0];
|
||||
case 0x3c: /* TBPR */
|
||||
return s->prescale[1];
|
||||
case 0x40: /* TAPMR */
|
||||
return s->match_prescale[0];
|
||||
case 0x44: /* TBPMR */
|
||||
return s->match_prescale[1];
|
||||
case 0x48: /* TAR */
|
||||
if (s->config == 1) {
|
||||
return s->rtc;
|
||||
}
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"GPTM: read of TAR but timer read not supported\n");
|
||||
return 0;
|
||||
case 0x4c: /* TBR */
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"GPTM: read of TBR but timer read not supported\n");
|
||||
return 0;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"GPTM: read at bad offset 0x02%" HWADDR_PRIx "\n",
|
||||
offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void gptm_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
gptm_state *s = (gptm_state *)opaque;
|
||||
uint32_t oldval;
|
||||
|
||||
/*
|
||||
* The timers should be disabled before changing the configuration.
|
||||
* We take advantage of this and defer everything until the timer
|
||||
* is enabled.
|
||||
*/
|
||||
switch (offset) {
|
||||
case 0x00: /* CFG */
|
||||
s->config = value;
|
||||
break;
|
||||
case 0x04: /* TAMR */
|
||||
s->mode[0] = value;
|
||||
break;
|
||||
case 0x08: /* TBMR */
|
||||
s->mode[1] = value;
|
||||
break;
|
||||
case 0x0c: /* CTL */
|
||||
oldval = s->control;
|
||||
s->control = value;
|
||||
/* TODO: Implement pause. */
|
||||
if ((oldval ^ value) & 1) {
|
||||
if (value & 1) {
|
||||
gptm_reload(s, 0, 1);
|
||||
} else {
|
||||
gptm_stop(s, 0);
|
||||
}
|
||||
}
|
||||
if (((oldval ^ value) & 0x100) && s->config >= 4) {
|
||||
if (value & 0x100) {
|
||||
gptm_reload(s, 1, 1);
|
||||
} else {
|
||||
gptm_stop(s, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x18: /* IMR */
|
||||
s->mask = value & 0x77;
|
||||
gptm_update_irq(s);
|
||||
break;
|
||||
case 0x24: /* CR */
|
||||
s->state &= ~value;
|
||||
break;
|
||||
case 0x28: /* TAILR */
|
||||
s->load[0] = value & 0xffff;
|
||||
if (s->config < 4) {
|
||||
s->load[1] = value >> 16;
|
||||
}
|
||||
break;
|
||||
case 0x2c: /* TBILR */
|
||||
s->load[1] = value & 0xffff;
|
||||
break;
|
||||
case 0x30: /* TAMARCHR */
|
||||
s->match[0] = value & 0xffff;
|
||||
if (s->config < 4) {
|
||||
s->match[1] = value >> 16;
|
||||
}
|
||||
break;
|
||||
case 0x34: /* TBMATCHR */
|
||||
s->match[1] = value >> 16;
|
||||
break;
|
||||
case 0x38: /* TAPR */
|
||||
s->prescale[0] = value;
|
||||
break;
|
||||
case 0x3c: /* TBPR */
|
||||
s->prescale[1] = value;
|
||||
break;
|
||||
case 0x40: /* TAPMR */
|
||||
s->match_prescale[0] = value;
|
||||
break;
|
||||
case 0x44: /* TBPMR */
|
||||
s->match_prescale[0] = value;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"GPTM: write at bad offset 0x02%" HWADDR_PRIx "\n",
|
||||
offset);
|
||||
}
|
||||
gptm_update_irq(s);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps gptm_ops = {
|
||||
.read = gptm_read,
|
||||
.write = gptm_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_stellaris_gptm = {
|
||||
.name = "stellaris_gptm",
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 2,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(config, gptm_state),
|
||||
VMSTATE_UINT32_ARRAY(mode, gptm_state, 2),
|
||||
VMSTATE_UINT32(control, gptm_state),
|
||||
VMSTATE_UINT32(state, gptm_state),
|
||||
VMSTATE_UINT32(mask, gptm_state),
|
||||
VMSTATE_UNUSED(8),
|
||||
VMSTATE_UINT32_ARRAY(load, gptm_state, 2),
|
||||
VMSTATE_UINT32_ARRAY(match, gptm_state, 2),
|
||||
VMSTATE_UINT32_ARRAY(prescale, gptm_state, 2),
|
||||
VMSTATE_UINT32_ARRAY(match_prescale, gptm_state, 2),
|
||||
VMSTATE_UINT32(rtc, gptm_state),
|
||||
VMSTATE_INT64_ARRAY(tick, gptm_state, 2),
|
||||
VMSTATE_TIMER_PTR_ARRAY(timer, gptm_state, 2),
|
||||
VMSTATE_CLOCK(clk, gptm_state),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void stellaris_gptm_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
gptm_state *s = STELLARIS_GPTM(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
qdev_init_gpio_out(dev, &s->trigger, 1);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &gptm_ops, s,
|
||||
"gptm", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
|
||||
s->opaque[0] = s->opaque[1] = s;
|
||||
|
||||
/*
|
||||
* TODO: in an ideal world we would model the effects of changing
|
||||
* the input clock frequency while the countdown timer is active.
|
||||
* The best way to do this would be to convert the device to use
|
||||
* ptimer instead of hand-rolling its own timer. This would also
|
||||
* make it easy to implement reading the current count from the
|
||||
* TAR and TBR registers.
|
||||
*/
|
||||
s->clk = qdev_init_clock_in(dev, "clk", NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static void stellaris_gptm_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
gptm_state *s = STELLARIS_GPTM(dev);
|
||||
|
||||
if (!clock_has_source(s->clk)) {
|
||||
error_setg(errp, "stellaris-gptm: clk must be connected");
|
||||
return;
|
||||
}
|
||||
|
||||
s->timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[0]);
|
||||
s->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[1]);
|
||||
}
|
||||
|
||||
static void stellaris_gptm_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->vmsd = &vmstate_stellaris_gptm;
|
||||
dc->realize = stellaris_gptm_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo stellaris_gptm_info = {
|
||||
.name = TYPE_STELLARIS_GPTM,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(gptm_state),
|
||||
.instance_init = stellaris_gptm_init,
|
||||
.class_init = stellaris_gptm_class_init,
|
||||
};
|
||||
|
||||
static void stellaris_gptm_register_types(void)
|
||||
{
|
||||
type_register_static(&stellaris_gptm_info);
|
||||
}
|
||||
|
||||
type_init(stellaris_gptm_register_types)
|
|
@ -12,8 +12,10 @@
|
|||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/intc/armv7m_nvic.h"
|
||||
#include "hw/misc/armv7m_ras.h"
|
||||
#include "target/arm/idau.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/clock.h"
|
||||
|
||||
#define TYPE_BITBAND "ARM-bitband-memory"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(BitBandState, BITBAND)
|
||||
|
@ -50,6 +52,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(ARMv7MState, ARMV7M)
|
|||
* + Property "vfp": enable VFP (forwarded to CPU object)
|
||||
* + Property "dsp": enable DSP (forwarded to CPU object)
|
||||
* + Property "enable-bitband": expose bitbanded IO
|
||||
* + Clock input "refclk" is the external reference clock for the systick timers
|
||||
* + Clock input "cpuclk" is the main CPU clock
|
||||
*/
|
||||
struct ARMv7MState {
|
||||
/*< private >*/
|
||||
|
@ -58,11 +62,31 @@ struct ARMv7MState {
|
|||
NVICState nvic;
|
||||
BitBandState bitband[ARMV7M_NUM_BITBANDS];
|
||||
ARMCPU *cpu;
|
||||
ARMv7MRAS ras;
|
||||
SysTickState systick[M_REG_NUM_BANKS];
|
||||
|
||||
/* MemoryRegion we pass to the CPU, with our devices layered on
|
||||
* top of the ones the board provides in board_memory.
|
||||
*/
|
||||
MemoryRegion container;
|
||||
/*
|
||||
* MemoryRegion which passes the transaction to either the S or the
|
||||
* NS systick device depending on the transaction attributes
|
||||
*/
|
||||
MemoryRegion systickmem;
|
||||
/*
|
||||
* MemoryRegion which enforces the S/NS handling of the systick
|
||||
* device NS alias region and passes the transaction to the
|
||||
* NS systick device if appropriate.
|
||||
*/
|
||||
MemoryRegion systick_ns_mem;
|
||||
/* Ditto, for the sysregs region provided by the NVIC */
|
||||
MemoryRegion sysreg_ns_mem;
|
||||
/* MR providing default PPB behaviour */
|
||||
MemoryRegion defaultmem;
|
||||
|
||||
Clock *refclk;
|
||||
Clock *cpuclk;
|
||||
|
||||
/* Properties */
|
||||
char *cpu_type;
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "hw/misc/msf2-sysreg.h"
|
||||
#include "hw/ssi/mss-spi.h"
|
||||
#include "hw/net/msf2-emac.h"
|
||||
#include "hw/clock.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_MSF2_SOC "msf2-soc"
|
||||
|
@ -57,7 +58,8 @@ struct MSF2State {
|
|||
uint64_t envm_size;
|
||||
uint64_t esram_size;
|
||||
|
||||
uint32_t m3clk;
|
||||
Clock *m3clk;
|
||||
Clock *refclk;
|
||||
uint8_t apb0div;
|
||||
uint8_t apb1div;
|
||||
|
||||
|
@ -65,6 +67,10 @@ struct MSF2State {
|
|||
MSSTimerState timer;
|
||||
MSSSpiState spi[MSF2_NUM_SPIS];
|
||||
MSF2EmacState emac;
|
||||
|
||||
MemoryRegion nvm;
|
||||
MemoryRegion nvm_alias;
|
||||
MemoryRegion sram;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "hw/gpio/nrf51_gpio.h"
|
||||
#include "hw/nvram/nrf51_nvm.h"
|
||||
#include "hw/timer/nrf51_timer.h"
|
||||
#include "hw/clock.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_NRF51_SOC "nrf51-soc"
|
||||
|
@ -50,6 +51,7 @@ struct NRF51State {
|
|||
|
||||
MemoryRegion container;
|
||||
|
||||
Clock *sysclk;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "hw/ssi/stm32f2xx_spi.h"
|
||||
#include "hw/arm/armv7m.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/clock.h"
|
||||
|
||||
#define TYPE_STM32F100_SOC "stm32f100-soc"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(STM32F100State, STM32F100_SOC)
|
||||
|
@ -52,6 +53,13 @@ struct STM32F100State {
|
|||
|
||||
STM32F2XXUsartState usart[STM_NUM_USARTS];
|
||||
STM32F2XXSPIState spi[STM_NUM_SPIS];
|
||||
|
||||
MemoryRegion sram;
|
||||
MemoryRegion flash;
|
||||
MemoryRegion flash_alias;
|
||||
|
||||
Clock *sysclk;
|
||||
Clock *refclk;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "hw/or-irq.h"
|
||||
#include "hw/ssi/stm32f2xx_spi.h"
|
||||
#include "hw/arm/armv7m.h"
|
||||
#include "hw/clock.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_STM32F205_SOC "stm32f205-soc"
|
||||
|
@ -63,6 +64,13 @@ struct STM32F205State {
|
|||
STM32F2XXSPIState spi[STM_NUM_SPIS];
|
||||
|
||||
qemu_or_irq *adc_irqs;
|
||||
|
||||
MemoryRegion sram;
|
||||
MemoryRegion flash;
|
||||
MemoryRegion flash_alias;
|
||||
|
||||
Clock *sysclk;
|
||||
Clock *refclk;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -68,6 +68,9 @@ struct STM32F405State {
|
|||
MemoryRegion sram;
|
||||
MemoryRegion flash;
|
||||
MemoryRegion flash_alias;
|
||||
|
||||
Clock *sysclk;
|
||||
Clock *refclk;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -353,6 +353,9 @@ struct MachineState {
|
|||
} \
|
||||
type_init(machine_initfn##_register_types)
|
||||
|
||||
extern GlobalProperty hw_compat_6_1[];
|
||||
extern const size_t hw_compat_6_1_len;
|
||||
|
||||
extern GlobalProperty hw_compat_6_0[];
|
||||
extern const size_t hw_compat_6_0_len;
|
||||
|
||||
|
|
|
@ -81,6 +81,10 @@ struct Clock {
|
|||
void *callback_opaque;
|
||||
unsigned int callback_events;
|
||||
|
||||
/* Ratio of the parent clock to run the child clocks at */
|
||||
uint32_t multiplier;
|
||||
uint32_t divider;
|
||||
|
||||
/* Clocks are organized in a clock tree */
|
||||
Clock *source;
|
||||
QLIST_HEAD(, Clock) children;
|
||||
|
@ -350,4 +354,29 @@ static inline bool clock_is_enabled(const Clock *clk)
|
|||
*/
|
||||
char *clock_display_freq(Clock *clk);
|
||||
|
||||
/**
|
||||
* clock_set_mul_div: set multiplier/divider for child clocks
|
||||
* @clk: clock
|
||||
* @multiplier: multiplier value
|
||||
* @divider: divider value
|
||||
*
|
||||
* By default, a Clock's children will all run with the same period
|
||||
* as their parent. This function allows you to adjust the multiplier
|
||||
* and divider used to derive the child clock frequency.
|
||||
* For example, setting a multiplier of 2 and a divider of 3
|
||||
* will run child clocks with a period 2/3 of the parent clock,
|
||||
* so if the parent clock is an 8MHz clock the children will
|
||||
* be 12MHz.
|
||||
*
|
||||
* Setting the multiplier to 0 will stop the child clocks.
|
||||
* Setting the divider to 0 is a programming error (diagnosed with
|
||||
* an assertion failure).
|
||||
* Setting a multiplier value that results in the child period
|
||||
* overflowing is not diagnosed.
|
||||
*
|
||||
* Note that this function does not call clock_propagate(); the
|
||||
* caller should do that if necessary.
|
||||
*/
|
||||
void clock_set_mul_div(Clock *clk, uint32_t multiplier, uint32_t divider);
|
||||
|
||||
#endif /* QEMU_HW_CLOCK_H */
|
||||
|
|
|
@ -196,6 +196,9 @@ void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size);
|
|||
void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid,
|
||||
const CPUArchIdList *apic_ids, GArray *entry);
|
||||
|
||||
extern GlobalProperty pc_compat_6_1[];
|
||||
extern const size_t pc_compat_6_1_len;
|
||||
|
||||
extern GlobalProperty pc_compat_6_0[];
|
||||
extern const size_t pc_compat_6_0_len;
|
||||
|
||||
|
|
|
@ -80,18 +80,10 @@ struct NVICState {
|
|||
int vectpending_prio; /* group prio of the exeception in vectpending */
|
||||
|
||||
MemoryRegion sysregmem;
|
||||
MemoryRegion sysreg_ns_mem;
|
||||
MemoryRegion systickmem;
|
||||
MemoryRegion systick_ns_mem;
|
||||
MemoryRegion ras_mem;
|
||||
MemoryRegion container;
|
||||
MemoryRegion defaultmem;
|
||||
|
||||
uint32_t num_irq;
|
||||
qemu_irq excpout;
|
||||
qemu_irq sysresetreq;
|
||||
|
||||
SysTickState systick[M_REG_NUM_BANKS];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Arm M-profile RAS (Reliability, Availability and Serviceability) block
|
||||
*
|
||||
* Copyright (c) 2021 Linaro Limited
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a model of the RAS register block of an M-profile CPU
|
||||
* (the registers starting at 0xE0005000 with ERRFRn).
|
||||
*
|
||||
* QEMU interface:
|
||||
* + sysbus MMIO region 0: the register bank
|
||||
*
|
||||
* The QEMU implementation currently provides "minimal RAS" only.
|
||||
*/
|
||||
|
||||
#ifndef HW_MISC_ARMV7M_RAS_H
|
||||
#define HW_MISC_ARMV7M_RAS_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_ARMV7M_RAS "armv7m-ras"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(ARMv7MRAS, ARMV7M_RAS)
|
||||
|
||||
struct ARMv7MRAS {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
/*< public >*/
|
||||
MemoryRegion iomem;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -15,11 +15,23 @@
|
|||
#include "hw/sysbus.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/ptimer.h"
|
||||
#include "hw/clock.h"
|
||||
|
||||
#define TYPE_SYSTICK "armv7m_systick"
|
||||
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(SysTickState, SYSTICK)
|
||||
|
||||
/*
|
||||
* QEMU interface:
|
||||
* + sysbus MMIO region 0 is the register interface (covering
|
||||
* the registers which are mapped at address 0xE000E010)
|
||||
* + sysbus IRQ 0 is the interrupt line to the NVIC
|
||||
* + Clock input "refclk" is the external reference clock
|
||||
* (used when SYST_CSR.CLKSOURCE == 0)
|
||||
* + Clock input "cpuclk" is the main CPU clock
|
||||
* (used when SYST_CSR.CLKSOURCE == 1)
|
||||
*/
|
||||
|
||||
struct SysTickState {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
|
@ -31,28 +43,8 @@ struct SysTickState {
|
|||
ptimer_state *ptimer;
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
Clock *refclk;
|
||||
Clock *cpuclk;
|
||||
};
|
||||
|
||||
/*
|
||||
* Multiplication factor to convert from system clock ticks to qemu timer
|
||||
* ticks. This should be set (by board code, usually) to a value
|
||||
* equal to NANOSECONDS_PER_SECOND / frq, where frq is the clock frequency
|
||||
* in Hz of the CPU.
|
||||
*
|
||||
* This value is used by the systick device when it is running in
|
||||
* its "use the CPU clock" mode (ie when SYST_CSR.CLKSOURCE == 1) to
|
||||
* set how fast the timer should tick.
|
||||
*
|
||||
* TODO: we should refactor this so that rather than using a global
|
||||
* we use a device property or something similar. This is complicated
|
||||
* because (a) the property would need to be plumbed through from the
|
||||
* board code down through various layers to the systick device
|
||||
* and (b) the property needs to be modifiable after realize, because
|
||||
* the stellaris board uses this to implement the behaviour where the
|
||||
* guest can reprogram the PLL registers to downclock the CPU, and the
|
||||
* systick device needs to react accordingly. Possibly this should
|
||||
* be deferred until we have a good API for modelling clock trees.
|
||||
*/
|
||||
extern int system_clock_scale;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Luminary Micro Stellaris General Purpose Timer Module
|
||||
*
|
||||
* Copyright (c) 2006 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licensed under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef HW_TIMER_STELLARIS_GPTM_H
|
||||
#define HW_TIMER_STELLARIS_GPTM_H
|
||||
|
||||
#include "qom/object.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/clock.h"
|
||||
|
||||
#define TYPE_STELLARIS_GPTM "stellaris-gptm"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(gptm_state, STELLARIS_GPTM)
|
||||
|
||||
/*
|
||||
* QEMU interface:
|
||||
* + sysbus MMIO region 0: register bank
|
||||
* + sysbus IRQ 0: timer interrupt
|
||||
* + unnamed GPIO output 0: trigger output for the ADC
|
||||
* + Clock input "clk": the 32-bit countdown timer runs at this speed
|
||||
*/
|
||||
struct gptm_state {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
uint32_t config;
|
||||
uint32_t mode[2];
|
||||
uint32_t control;
|
||||
uint32_t state;
|
||||
uint32_t mask;
|
||||
uint32_t load[2];
|
||||
uint32_t match[2];
|
||||
uint32_t prescale[2];
|
||||
uint32_t match_prescale[2];
|
||||
uint32_t rtc;
|
||||
int64_t tick[2];
|
||||
struct gptm_state *opaque[2];
|
||||
QEMUTimer *timer[2];
|
||||
/* The timers have an alternate output used to trigger the ADC. */
|
||||
qemu_irq trigger;
|
||||
qemu_irq irq;
|
||||
Clock *clk;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -841,10 +841,58 @@ static void aarch64_max_initfn(Object *obj)
|
|||
cpu_max_set_sve_max_vq, NULL, NULL);
|
||||
}
|
||||
|
||||
static void aarch64_a64fx_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
|
||||
cpu->dtb_compatible = "arm,a64fx";
|
||||
set_feature(&cpu->env, ARM_FEATURE_V8);
|
||||
set_feature(&cpu->env, ARM_FEATURE_NEON);
|
||||
set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER);
|
||||
set_feature(&cpu->env, ARM_FEATURE_AARCH64);
|
||||
set_feature(&cpu->env, ARM_FEATURE_EL2);
|
||||
set_feature(&cpu->env, ARM_FEATURE_EL3);
|
||||
set_feature(&cpu->env, ARM_FEATURE_PMU);
|
||||
cpu->midr = 0x461f0010;
|
||||
cpu->revidr = 0x00000000;
|
||||
cpu->ctr = 0x86668006;
|
||||
cpu->reset_sctlr = 0x30000180;
|
||||
cpu->isar.id_aa64pfr0 = 0x0000000101111111; /* No RAS Extensions */
|
||||
cpu->isar.id_aa64pfr1 = 0x0000000000000000;
|
||||
cpu->isar.id_aa64dfr0 = 0x0000000010305408;
|
||||
cpu->isar.id_aa64dfr1 = 0x0000000000000000;
|
||||
cpu->id_aa64afr0 = 0x0000000000000000;
|
||||
cpu->id_aa64afr1 = 0x0000000000000000;
|
||||
cpu->isar.id_aa64mmfr0 = 0x0000000000001122;
|
||||
cpu->isar.id_aa64mmfr1 = 0x0000000011212100;
|
||||
cpu->isar.id_aa64mmfr2 = 0x0000000000001011;
|
||||
cpu->isar.id_aa64isar0 = 0x0000000010211120;
|
||||
cpu->isar.id_aa64isar1 = 0x0000000000010001;
|
||||
cpu->isar.id_aa64zfr0 = 0x0000000000000000;
|
||||
cpu->clidr = 0x0000000080000023;
|
||||
cpu->ccsidr[0] = 0x7007e01c; /* 64KB L1 dcache */
|
||||
cpu->ccsidr[1] = 0x2007e01c; /* 64KB L1 icache */
|
||||
cpu->ccsidr[2] = 0x70ffe07c; /* 8MB L2 cache */
|
||||
cpu->dcz_blocksize = 6; /* 256 bytes */
|
||||
cpu->gic_num_lrs = 4;
|
||||
cpu->gic_vpribits = 5;
|
||||
cpu->gic_vprebits = 5;
|
||||
|
||||
/* Suppport of A64FX's vector length are 128,256 and 512bit only */
|
||||
aarch64_add_sve_properties(obj);
|
||||
bitmap_zero(cpu->sve_vq_supported, ARM_MAX_VQ);
|
||||
set_bit(0, cpu->sve_vq_supported); /* 128bit */
|
||||
set_bit(1, cpu->sve_vq_supported); /* 256bit */
|
||||
set_bit(3, cpu->sve_vq_supported); /* 512bit */
|
||||
|
||||
/* TODO: Add A64FX specific HPC extension registers */
|
||||
}
|
||||
|
||||
static const ARMCPUInfo aarch64_cpus[] = {
|
||||
{ .name = "cortex-a57", .initfn = aarch64_a57_initfn },
|
||||
{ .name = "cortex-a53", .initfn = aarch64_a53_initfn },
|
||||
{ .name = "cortex-a72", .initfn = aarch64_a72_initfn },
|
||||
{ .name = "a64fx", .initfn = aarch64_a64fx_initfn },
|
||||
{ .name = "max", .initfn = aarch64_max_initfn },
|
||||
};
|
||||
|
||||
|
|
|
@ -654,12 +654,9 @@ static void cortex_m55_initfn(Object *obj)
|
|||
cpu->revidr = 0;
|
||||
cpu->pmsav7_dregion = 16;
|
||||
cpu->sau_sregion = 8;
|
||||
/*
|
||||
* These are the MVFR* values for the FPU, no MVE configuration;
|
||||
* we will update them later when we implement MVE
|
||||
*/
|
||||
/* These are the MVFR* values for the FPU + full MVE configuration */
|
||||
cpu->isar.mvfr0 = 0x10110221;
|
||||
cpu->isar.mvfr1 = 0x12100011;
|
||||
cpu->isar.mvfr1 = 0x12100211;
|
||||
cpu->isar.mvfr2 = 0x00000040;
|
||||
cpu->isar.id_pfr0 = 0x20000030;
|
||||
cpu->isar.id_pfr1 = 0x00000230;
|
||||
|
|
|
@ -177,6 +177,16 @@ DEF_HELPER_FLAGS_3(mve_vminab, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
|||
DEF_HELPER_FLAGS_3(mve_vminah, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_3(mve_vminaw, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vcvt_rm_sh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(mve_vcvt_rm_uh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(mve_vcvt_rm_ss, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(mve_vcvt_rm_us, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vcvtb_sh, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_3(mve_vcvtt_sh, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_3(mve_vcvtb_hs, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_3(mve_vcvtt_hs, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vmovnbb, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_3(mve_vmovnbh, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_3(mve_vmovntb, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
|
@ -410,6 +420,60 @@ DEF_HELPER_FLAGS_4(mve_vhcadd270b, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
|||
DEF_HELPER_FLAGS_4(mve_vhcadd270h, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vhcadd270w, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vfaddh, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vfadds, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vfsubh, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vfsubs, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vfmulh, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vfmuls, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vfabdh, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vfabds, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vmaxnmh, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vmaxnms, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vminnmh, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vminnms, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vmaxnmah, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vmaxnmas, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vminnmah, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vminnmas, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vfcadd90h, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vfcadd90s, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vfcadd270h, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vfcadd270s, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vfmah, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vfmas, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vfmsh, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vfmss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vcmul0h, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vcmul0s, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vcmul90h, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vcmul90s, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vcmul180h, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vcmul180s, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vcmul270h, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vcmul270s, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vcmla0h, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vcmla0s, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vcmla90h, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vcmla90s, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vcmla180h, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vcmla180s, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vcmla270h, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_4(mve_vcmla270s, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vadd_scalarb, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(mve_vadd_scalarh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(mve_vadd_scalarw, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
|
@ -560,6 +624,18 @@ DEF_HELPER_FLAGS_3(mve_vminavb, TCG_CALL_NO_WG, i32, env, ptr, i32)
|
|||
DEF_HELPER_FLAGS_3(mve_vminavh, TCG_CALL_NO_WG, i32, env, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(mve_vminavw, TCG_CALL_NO_WG, i32, env, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vmaxnmvh, TCG_CALL_NO_WG, i32, env, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(mve_vmaxnmvs, TCG_CALL_NO_WG, i32, env, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vminnmvh, TCG_CALL_NO_WG, i32, env, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(mve_vminnmvs, TCG_CALL_NO_WG, i32, env, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vmaxnmavh, TCG_CALL_NO_WG, i32, env, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(mve_vmaxnmavs, TCG_CALL_NO_WG, i32, env, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vminnmavh, TCG_CALL_NO_WG, i32, env, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(mve_vminnmavs, TCG_CALL_NO_WG, i32, env, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vaddlv_s, TCG_CALL_NO_WG, i64, env, ptr, i64)
|
||||
DEF_HELPER_FLAGS_3(mve_vaddlv_u, TCG_CALL_NO_WG, i64, env, ptr, i64)
|
||||
|
||||
|
@ -746,3 +822,69 @@ DEF_HELPER_FLAGS_3(mve_vcmpgt_scalarw, TCG_CALL_NO_WG, void, env, ptr, i32)
|
|||
DEF_HELPER_FLAGS_3(mve_vcmple_scalarb, TCG_CALL_NO_WG, void, env, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(mve_vcmple_scalarh, TCG_CALL_NO_WG, void, env, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(mve_vcmple_scalarw, TCG_CALL_NO_WG, void, env, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmpeqh, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmpeqs, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmpneh, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmpnes, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmpgeh, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmpges, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmplth, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmplts, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmpgth, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmpgts, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmpleh, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmples, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmpeq_scalarh, TCG_CALL_NO_WG, void, env, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmpeq_scalars, TCG_CALL_NO_WG, void, env, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmpne_scalarh, TCG_CALL_NO_WG, void, env, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmpne_scalars, TCG_CALL_NO_WG, void, env, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmpge_scalarh, TCG_CALL_NO_WG, void, env, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmpge_scalars, TCG_CALL_NO_WG, void, env, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmplt_scalarh, TCG_CALL_NO_WG, void, env, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmplt_scalars, TCG_CALL_NO_WG, void, env, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmpgt_scalarh, TCG_CALL_NO_WG, void, env, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmpgt_scalars, TCG_CALL_NO_WG, void, env, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmple_scalarh, TCG_CALL_NO_WG, void, env, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(mve_vfcmple_scalars, TCG_CALL_NO_WG, void, env, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vfadd_scalarh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(mve_vfadd_scalars, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vfsub_scalarh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(mve_vfsub_scalars, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vfmul_scalarh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(mve_vfmul_scalars, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vfma_scalarh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(mve_vfma_scalars, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vfmas_scalarh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(mve_vfmas_scalars, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vcvt_sh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(mve_vcvt_uh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(mve_vcvt_hs, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(mve_vcvt_hu, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(mve_vcvt_sf, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(mve_vcvt_uf, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(mve_vcvt_fs, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(mve_vcvt_fu, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(mve_vrint_rm_h, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(mve_vrint_rm_s, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_3(mve_vrintx_h, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
DEF_HELPER_FLAGS_3(mve_vrintx_s, TCG_CALL_NO_WG, void, env, ptr, ptr)
|
||||
|
|
|
@ -26,6 +26,14 @@
|
|||
# VQDMULL has size in bit 28: 0 for 16 bit, 1 for 32 bit
|
||||
%size_28 28:1 !function=plus_1
|
||||
|
||||
# 2 operand fp insns have size in bit 20: 1 for 16 bit, 0 for 32 bit,
|
||||
# like Neon FP insns.
|
||||
%2op_fp_size 20:1 !function=neon_3same_fp_size
|
||||
# VCADD is an exception, where bit 20 is 0 for 16 bit and 1 for 32 bit
|
||||
%2op_fp_size_rev 20:1 !function=plus_1
|
||||
# FP scalars have size in bit 28, 1 for 16 bit, 0 for 32 bit
|
||||
%2op_fp_scalar_size 28:1 !function=neon_3same_fp_size
|
||||
|
||||
# 1imm format immediate
|
||||
%imm_28_16_0 28:1 16:3 0:4
|
||||
|
||||
|
@ -116,8 +124,34 @@
|
|||
@vcmp_scalar .... .... .. size:2 qn:3 . .... .... .... rm:4 &vcmp_scalar \
|
||||
mask=%mask_22_13
|
||||
|
||||
@vcmp_fp .... .... .... qn:3 . .... .... .... .... &vcmp \
|
||||
qm=%qm size=%2op_fp_scalar_size mask=%mask_22_13
|
||||
|
||||
# Bit 28 is a 2op_fp_scalar_size bit, but we do not decode it in this
|
||||
# format to avoid complicated overlapping-instruction-groups
|
||||
@vcmp_fp_scalar .... .... .... qn:3 . .... .... .... rm:4 &vcmp_scalar \
|
||||
mask=%mask_22_13
|
||||
|
||||
@vmaxv .... .... .... size:2 .. rda:4 .... .... .... &vmaxv qm=%qm
|
||||
|
||||
@2op_fp .... .... .... .... .... .... .... .... &2op \
|
||||
qd=%qd qn=%qn qm=%qm size=%2op_fp_size
|
||||
|
||||
@2op_fp_size_rev .... .... .... .... .... .... .... .... &2op \
|
||||
qd=%qd qn=%qn qm=%qm size=%2op_fp_size_rev
|
||||
|
||||
# 2-operand, but Qd and Qn share a field. Size is in bit 28, but we
|
||||
# don't decode it in this format
|
||||
@vmaxnma .... .... .... .... .... .... .... .... &2op \
|
||||
qd=%qd qn=%qd qm=%qm
|
||||
|
||||
# Here also we don't decode the bit 28 size in the format to avoid
|
||||
# awkward nested overlap groups
|
||||
@vmaxnmv .... .... .... .... rda:4 .... .... .... &vmaxv qm=%qm
|
||||
|
||||
@2op_fp_scalar .... .... .... .... .... .... .... rm:4 &2scalar \
|
||||
qd=%qd qn=%qn size=%2op_fp_scalar_size
|
||||
|
||||
# Vector loads and stores
|
||||
|
||||
# Widening loads and narrowing stores:
|
||||
|
@ -187,6 +221,10 @@ VMUL 1110 1111 0 . .. ... 0 ... 0 1001 . 1 . 1 ... 0 @2op
|
|||
# The VSHLL T2 encoding is not a @2op pattern, but is here because it
|
||||
# overlaps what would be size=0b11 VMULH/VRMULH
|
||||
{
|
||||
VCVTB_SH 111 0 1110 0 . 11 1111 ... 0 1110 0 0 . 0 ... 1 @1op_nosz
|
||||
|
||||
VMAXNMA 111 0 1110 0 . 11 1111 ... 0 1110 1 0 . 0 ... 1 @vmaxnma size=2
|
||||
|
||||
VSHLL_BS 111 0 1110 0 . 11 .. 01 ... 0 1110 0 0 . 0 ... 1 @2_shll_esize_b
|
||||
VSHLL_BS 111 0 1110 0 . 11 .. 01 ... 0 1110 0 0 . 0 ... 1 @2_shll_esize_h
|
||||
|
||||
|
@ -199,6 +237,10 @@ VMUL 1110 1111 0 . .. ... 0 ... 0 1001 . 1 . 1 ... 0 @2op
|
|||
}
|
||||
|
||||
{
|
||||
VCVTB_HS 111 1 1110 0 . 11 1111 ... 0 1110 0 0 . 0 ... 1 @1op_nosz
|
||||
|
||||
VMAXNMA 111 1 1110 0 . 11 1111 ... 0 1110 1 0 . 0 ... 1 @vmaxnma size=1
|
||||
|
||||
VSHLL_BU 111 1 1110 0 . 11 .. 01 ... 0 1110 0 0 . 0 ... 1 @2_shll_esize_b
|
||||
VSHLL_BU 111 1 1110 0 . 11 .. 01 ... 0 1110 0 0 . 0 ... 1 @2_shll_esize_h
|
||||
|
||||
|
@ -209,6 +251,9 @@ VMUL 1110 1111 0 . .. ... 0 ... 0 1001 . 1 . 1 ... 0 @2op
|
|||
}
|
||||
|
||||
{
|
||||
VCVTT_SH 111 0 1110 0 . 11 1111 ... 1 1110 0 0 . 0 ... 1 @1op_nosz
|
||||
|
||||
VMINNMA 111 0 1110 0 . 11 1111 ... 1 1110 1 0 . 0 ... 1 @vmaxnma size=2
|
||||
VSHLL_TS 111 0 1110 0 . 11 .. 01 ... 1 1110 0 0 . 0 ... 1 @2_shll_esize_b
|
||||
VSHLL_TS 111 0 1110 0 . 11 .. 01 ... 1 1110 0 0 . 0 ... 1 @2_shll_esize_h
|
||||
|
||||
|
@ -221,6 +266,9 @@ VMUL 1110 1111 0 . .. ... 0 ... 0 1001 . 1 . 1 ... 0 @2op
|
|||
}
|
||||
|
||||
{
|
||||
VCVTT_HS 111 1 1110 0 . 11 1111 ... 1 1110 0 0 . 0 ... 1 @1op_nosz
|
||||
|
||||
VMINNMA 111 1 1110 0 . 11 1111 ... 1 1110 1 0 . 0 ... 1 @vmaxnma size=1
|
||||
VSHLL_TU 111 1 1110 0 . 11 .. 01 ... 1 1110 0 0 . 0 ... 1 @2_shll_esize_b
|
||||
VSHLL_TU 111 1 1110 0 . 11 .. 01 ... 1 1110 0 0 . 0 ... 1 @2_shll_esize_h
|
||||
|
||||
|
@ -274,15 +322,29 @@ VQSHL_U 111 1 1111 0 . .. ... 0 ... 0 0100 . 1 . 1 ... 0 @2op_rev
|
|||
VQRSHL_S 111 0 1111 0 . .. ... 0 ... 0 0101 . 1 . 1 ... 0 @2op_rev
|
||||
VQRSHL_U 111 1 1111 0 . .. ... 0 ... 0 0101 . 1 . 1 ... 0 @2op_rev
|
||||
|
||||
VQDMLADH 1110 1110 0 . .. ... 0 ... 0 1110 . 0 . 0 ... 0 @2op
|
||||
VQDMLADHX 1110 1110 0 . .. ... 0 ... 1 1110 . 0 . 0 ... 0 @2op
|
||||
VQRDMLADH 1110 1110 0 . .. ... 0 ... 0 1110 . 0 . 0 ... 1 @2op
|
||||
VQRDMLADHX 1110 1110 0 . .. ... 0 ... 1 1110 . 0 . 0 ... 1 @2op
|
||||
{
|
||||
VCMUL0 111 . 1110 0 . 11 ... 0 ... 0 1110 . 0 . 0 ... 0 @2op_sz28
|
||||
VQDMLADH 1110 1110 0 . .. ... 0 ... 0 1110 . 0 . 0 ... 0 @2op
|
||||
VQDMLSDH 1111 1110 0 . .. ... 0 ... 0 1110 . 0 . 0 ... 0 @2op
|
||||
}
|
||||
|
||||
VQDMLSDH 1111 1110 0 . .. ... 0 ... 0 1110 . 0 . 0 ... 0 @2op
|
||||
VQDMLSDHX 1111 1110 0 . .. ... 0 ... 1 1110 . 0 . 0 ... 0 @2op
|
||||
VQRDMLSDH 1111 1110 0 . .. ... 0 ... 0 1110 . 0 . 0 ... 1 @2op
|
||||
VQRDMLSDHX 1111 1110 0 . .. ... 0 ... 1 1110 . 0 . 0 ... 1 @2op
|
||||
{
|
||||
VCMUL180 111 . 1110 0 . 11 ... 0 ... 1 1110 . 0 . 0 ... 0 @2op_sz28
|
||||
VQDMLADHX 111 0 1110 0 . .. ... 0 ... 1 1110 . 0 . 0 ... 0 @2op
|
||||
VQDMLSDHX 111 1 1110 0 . .. ... 0 ... 1 1110 . 0 . 0 ... 0 @2op
|
||||
}
|
||||
|
||||
{
|
||||
VCMUL90 111 . 1110 0 . 11 ... 0 ... 0 1110 . 0 . 0 ... 1 @2op_sz28
|
||||
VQRDMLADH 111 0 1110 0 . .. ... 0 ... 0 1110 . 0 . 0 ... 1 @2op
|
||||
VQRDMLSDH 111 1 1110 0 . .. ... 0 ... 0 1110 . 0 . 0 ... 1 @2op
|
||||
}
|
||||
|
||||
{
|
||||
VCMUL270 111 . 1110 0 . 11 ... 0 ... 1 1110 . 0 . 0 ... 1 @2op_sz28
|
||||
VQRDMLADHX 111 0 1110 0 . .. ... 0 ... 1 1110 . 0 . 0 ... 1 @2op
|
||||
VQRDMLSDHX 111 1 1110 0 . .. ... 0 ... 1 1110 . 0 . 0 ... 1 @2op
|
||||
}
|
||||
|
||||
VQDMULLB 111 . 1110 0 . 11 ... 0 ... 0 1111 . 0 . 0 ... 1 @2op_sz28
|
||||
VQDMULLT 111 . 1110 0 . 11 ... 0 ... 1 1111 . 0 . 0 ... 1 @2op_sz28
|
||||
|
@ -351,8 +413,10 @@ VDUP 1110 1110 1 0 10 ... 0 .... 1011 . 0 0 1 0000 @vdup size=2
|
|||
VIWDUP 1110 1110 0 . .. ... 1 ... 0 1111 . 110 ... . @viwdup
|
||||
}
|
||||
{
|
||||
VDDUP 1110 1110 0 . .. ... 1 ... 1 1111 . 110 111 . @vidup
|
||||
VDWDUP 1110 1110 0 . .. ... 1 ... 1 1111 . 110 ... . @viwdup
|
||||
VCMPGT_fp_scalar 1110 1110 0 . 11 ... 1 ... 1 1111 0110 .... @vcmp_fp_scalar size=2
|
||||
VCMPLE_fp_scalar 1110 1110 0 . 11 ... 1 ... 1 1111 1110 .... @vcmp_fp_scalar size=2
|
||||
VDDUP 1110 1110 0 . .. ... 1 ... 1 1111 . 110 111 . @vidup
|
||||
VDWDUP 1110 1110 0 . .. ... 1 ... 1 1111 . 110 ... . @viwdup
|
||||
}
|
||||
|
||||
# multiply-add long dual accumulate
|
||||
|
@ -398,25 +462,50 @@ VMLADAV_S 1110 1110 1111 ... 0 ... . 1111 . 0 . 0 ... 1 @vmladav_nosz
|
|||
VMLADAV_U 1111 1110 1111 ... 0 ... . 1111 . 0 . 0 ... 1 @vmladav_nosz
|
||||
|
||||
{
|
||||
VMAXV_S 1110 1110 1110 .. 10 .... 1111 0 0 . 0 ... 0 @vmaxv
|
||||
VMINV_S 1110 1110 1110 .. 10 .... 1111 1 0 . 0 ... 0 @vmaxv
|
||||
VMAXAV 1110 1110 1110 .. 00 .... 1111 0 0 . 0 ... 0 @vmaxv
|
||||
VMINAV 1110 1110 1110 .. 00 .... 1111 1 0 . 0 ... 0 @vmaxv
|
||||
[
|
||||
VMAXNMAV 1110 1110 1110 11 00 .... 1111 0 0 . 0 ... 0 @vmaxnmv size=2
|
||||
VMINNMAV 1110 1110 1110 11 00 .... 1111 1 0 . 0 ... 0 @vmaxnmv size=2
|
||||
VMAXNMV 1110 1110 1110 11 10 .... 1111 0 0 . 0 ... 0 @vmaxnmv size=2
|
||||
VMINNMV 1110 1110 1110 11 10 .... 1111 1 0 . 0 ... 0 @vmaxnmv size=2
|
||||
]
|
||||
[
|
||||
VMAXV_S 1110 1110 1110 .. 10 .... 1111 0 0 . 0 ... 0 @vmaxv
|
||||
VMINV_S 1110 1110 1110 .. 10 .... 1111 1 0 . 0 ... 0 @vmaxv
|
||||
VMAXAV 1110 1110 1110 .. 00 .... 1111 0 0 . 0 ... 0 @vmaxv
|
||||
VMINAV 1110 1110 1110 .. 00 .... 1111 1 0 . 0 ... 0 @vmaxv
|
||||
]
|
||||
VMLADAV_S 1110 1110 1111 ... 0 ... . 1111 . 0 . 0 ... 0 @vmladav_nosz
|
||||
VRMLALDAVH_S 1110 1110 1 ... ... 0 ... . 1111 . 0 . 0 ... 0 @vmlaldav_nosz
|
||||
}
|
||||
|
||||
{
|
||||
VMAXV_U 1111 1110 1110 .. 10 .... 1111 0 0 . 0 ... 0 @vmaxv
|
||||
VMINV_U 1111 1110 1110 .. 10 .... 1111 1 0 . 0 ... 0 @vmaxv
|
||||
[
|
||||
VMAXNMAV 1111 1110 1110 11 00 .... 1111 0 0 . 0 ... 0 @vmaxnmv size=1
|
||||
VMINNMAV 1111 1110 1110 11 00 .... 1111 1 0 . 0 ... 0 @vmaxnmv size=1
|
||||
VMAXNMV 1111 1110 1110 11 10 .... 1111 0 0 . 0 ... 0 @vmaxnmv size=1
|
||||
VMINNMV 1111 1110 1110 11 10 .... 1111 1 0 . 0 ... 0 @vmaxnmv size=1
|
||||
]
|
||||
[
|
||||
VMAXV_U 1111 1110 1110 .. 10 .... 1111 0 0 . 0 ... 0 @vmaxv
|
||||
VMINV_U 1111 1110 1110 .. 10 .... 1111 1 0 . 0 ... 0 @vmaxv
|
||||
]
|
||||
VMLADAV_U 1111 1110 1111 ... 0 ... . 1111 . 0 . 0 ... 0 @vmladav_nosz
|
||||
VRMLALDAVH_U 1111 1110 1 ... ... 0 ... . 1111 . 0 . 0 ... 0 @vmlaldav_nosz
|
||||
}
|
||||
|
||||
# Scalar operations
|
||||
|
||||
VADD_scalar 1110 1110 0 . .. ... 1 ... 0 1111 . 100 .... @2scalar
|
||||
VSUB_scalar 1110 1110 0 . .. ... 1 ... 1 1111 . 100 .... @2scalar
|
||||
{
|
||||
VCMPEQ_fp_scalar 1110 1110 0 . 11 ... 1 ... 0 1111 0100 .... @vcmp_fp_scalar size=2
|
||||
VCMPNE_fp_scalar 1110 1110 0 . 11 ... 1 ... 0 1111 1100 .... @vcmp_fp_scalar size=2
|
||||
VADD_scalar 1110 1110 0 . .. ... 1 ... 0 1111 . 100 .... @2scalar
|
||||
}
|
||||
|
||||
{
|
||||
VCMPLT_fp_scalar 1110 1110 0 . 11 ... 1 ... 1 1111 1100 .... @vcmp_fp_scalar size=2
|
||||
VCMPGE_fp_scalar 1110 1110 0 . 11 ... 1 ... 1 1111 0100 .... @vcmp_fp_scalar size=2
|
||||
VSUB_scalar 1110 1110 0 . .. ... 1 ... 1 1111 . 100 .... @2scalar
|
||||
}
|
||||
|
||||
{
|
||||
VSHL_S_scalar 1110 1110 0 . 11 .. 01 ... 1 1110 0110 .... @shl_scalar
|
||||
|
@ -434,10 +523,17 @@ VSUB_scalar 1110 1110 0 . .. ... 1 ... 1 1111 . 100 .... @2scalar
|
|||
VBRSR 1111 1110 0 . .. ... 1 ... 1 1110 . 110 .... @2scalar
|
||||
}
|
||||
|
||||
VHADD_S_scalar 1110 1110 0 . .. ... 0 ... 0 1111 . 100 .... @2scalar
|
||||
VHADD_U_scalar 1111 1110 0 . .. ... 0 ... 0 1111 . 100 .... @2scalar
|
||||
VHSUB_S_scalar 1110 1110 0 . .. ... 0 ... 1 1111 . 100 .... @2scalar
|
||||
VHSUB_U_scalar 1111 1110 0 . .. ... 0 ... 1 1111 . 100 .... @2scalar
|
||||
{
|
||||
VADD_fp_scalar 111 . 1110 0 . 11 ... 0 ... 0 1111 . 100 .... @2op_fp_scalar
|
||||
VHADD_S_scalar 1110 1110 0 . .. ... 0 ... 0 1111 . 100 .... @2scalar
|
||||
VHADD_U_scalar 1111 1110 0 . .. ... 0 ... 0 1111 . 100 .... @2scalar
|
||||
}
|
||||
|
||||
{
|
||||
VSUB_fp_scalar 111 . 1110 0 . 11 ... 0 ... 1 1111 . 100 .... @2op_fp_scalar
|
||||
VHSUB_S_scalar 1110 1110 0 . .. ... 0 ... 1 1111 . 100 .... @2scalar
|
||||
VHSUB_U_scalar 1111 1110 0 . .. ... 0 ... 1 1111 . 100 .... @2scalar
|
||||
}
|
||||
|
||||
{
|
||||
VQADD_S_scalar 1110 1110 0 . .. ... 0 ... 0 1111 . 110 .... @2scalar
|
||||
|
@ -453,12 +549,23 @@ VHSUB_U_scalar 1111 1110 0 . .. ... 0 ... 1 1111 . 100 .... @2scalar
|
|||
size=%size_28
|
||||
}
|
||||
|
||||
VQDMULH_scalar 1110 1110 0 . .. ... 1 ... 0 1110 . 110 .... @2scalar
|
||||
VQRDMULH_scalar 1111 1110 0 . .. ... 1 ... 0 1110 . 110 .... @2scalar
|
||||
{
|
||||
VMUL_fp_scalar 111 . 1110 0 . 11 ... 1 ... 0 1110 . 110 .... @2op_fp_scalar
|
||||
VQDMULH_scalar 1110 1110 0 . .. ... 1 ... 0 1110 . 110 .... @2scalar
|
||||
VQRDMULH_scalar 1111 1110 0 . .. ... 1 ... 0 1110 . 110 .... @2scalar
|
||||
}
|
||||
|
||||
# The U bit (28) is don't-care because it does not affect the result
|
||||
VMLA 111- 1110 0 . .. ... 1 ... 0 1110 . 100 .... @2scalar
|
||||
VMLAS 111- 1110 0 . .. ... 1 ... 1 1110 . 100 .... @2scalar
|
||||
{
|
||||
VFMA_scalar 111 . 1110 0 . 11 ... 1 ... 0 1110 . 100 .... @2op_fp_scalar
|
||||
# The U bit (28) is don't-care because it does not affect the result
|
||||
VMLA 111 - 1110 0 . .. ... 1 ... 0 1110 . 100 .... @2scalar
|
||||
}
|
||||
|
||||
{
|
||||
VFMAS_scalar 111 . 1110 0 . 11 ... 1 ... 1 1110 . 100 .... @2op_fp_scalar
|
||||
# The U bit (28) is don't-care because it does not affect the result
|
||||
VMLAS 111 - 1110 0 . .. ... 1 ... 1 1110 . 100 .... @2scalar
|
||||
}
|
||||
|
||||
VQRDMLAH 1110 1110 0 . .. ... 0 ... 0 1110 . 100 .... @2scalar
|
||||
VQRDMLASH 1110 1110 0 . .. ... 0 ... 1 1110 . 100 .... @2scalar
|
||||
|
@ -591,27 +698,135 @@ VSHLC 111 0 1110 1 . 1 imm:5 ... 0 1111 1100 rdm:4 qd=%qd
|
|||
# Comparisons. We expand out the conditions which are split across
|
||||
# encodings T1, T2, T3 and the fc bits. These include VPT, which is
|
||||
# effectively "VCMP then VPST". A plain "VCMP" has a mask field of zero.
|
||||
VCMPEQ 1111 1110 0 . .. ... 1 ... 0 1111 0 0 . 0 ... 0 @vcmp
|
||||
VCMPNE 1111 1110 0 . .. ... 1 ... 0 1111 1 0 . 0 ... 0 @vcmp
|
||||
{
|
||||
VCMPEQ_fp 111 . 1110 0 . 11 ... 1 ... 0 1111 0 0 . 0 ... 0 @vcmp_fp
|
||||
VCMPEQ 111 1 1110 0 . .. ... 1 ... 0 1111 0 0 . 0 ... 0 @vcmp
|
||||
}
|
||||
|
||||
{
|
||||
VCMPNE_fp 111 . 1110 0 . 11 ... 1 ... 0 1111 1 0 . 0 ... 0 @vcmp_fp
|
||||
VCMPNE 111 1 1110 0 . .. ... 1 ... 0 1111 1 0 . 0 ... 0 @vcmp
|
||||
}
|
||||
|
||||
{
|
||||
VCMPGE_fp 111 . 1110 0 . 11 ... 1 ... 1 1111 0 0 . 0 ... 0 @vcmp_fp
|
||||
VCMPGE 111 1 1110 0 . .. ... 1 ... 1 1111 0 0 . 0 ... 0 @vcmp
|
||||
}
|
||||
|
||||
{
|
||||
VCMPLT_fp 111 . 1110 0 . 11 ... 1 ... 1 1111 1 0 . 0 ... 0 @vcmp_fp
|
||||
VCMPLT 111 1 1110 0 . .. ... 1 ... 1 1111 1 0 . 0 ... 0 @vcmp
|
||||
}
|
||||
|
||||
{
|
||||
VCMPGT_fp 111 . 1110 0 . 11 ... 1 ... 1 1111 0 0 . 0 ... 1 @vcmp_fp
|
||||
VCMPGT 111 1 1110 0 . .. ... 1 ... 1 1111 0 0 . 0 ... 1 @vcmp
|
||||
}
|
||||
|
||||
{
|
||||
VCMPLE_fp 111 . 1110 0 . 11 ... 1 ... 1 1111 1 0 . 0 ... 1 @vcmp_fp
|
||||
VCMPLE 1111 1110 0 . .. ... 1 ... 1 1111 1 0 . 0 ... 1 @vcmp
|
||||
}
|
||||
|
||||
{
|
||||
VPSEL 1111 1110 0 . 11 ... 1 ... 0 1111 . 0 . 0 ... 1 @2op_nosz
|
||||
VCMPCS 1111 1110 0 . .. ... 1 ... 0 1111 0 0 . 0 ... 1 @vcmp
|
||||
VCMPHI 1111 1110 0 . .. ... 1 ... 0 1111 1 0 . 0 ... 1 @vcmp
|
||||
}
|
||||
VCMPGE 1111 1110 0 . .. ... 1 ... 1 1111 0 0 . 0 ... 0 @vcmp
|
||||
VCMPLT 1111 1110 0 . .. ... 1 ... 1 1111 1 0 . 0 ... 0 @vcmp
|
||||
VCMPGT 1111 1110 0 . .. ... 1 ... 1 1111 0 0 . 0 ... 1 @vcmp
|
||||
VCMPLE 1111 1110 0 . .. ... 1 ... 1 1111 1 0 . 0 ... 1 @vcmp
|
||||
|
||||
{
|
||||
VPNOT 1111 1110 0 0 11 000 1 000 0 1111 0100 1101
|
||||
VPST 1111 1110 0 . 11 000 1 ... 0 1111 0100 1101 mask=%mask_22_13
|
||||
VCMPEQ_scalar 1111 1110 0 . .. ... 1 ... 0 1111 0 1 0 0 .... @vcmp_scalar
|
||||
VPNOT 1111 1110 0 0 11 000 1 000 0 1111 0100 1101
|
||||
VPST 1111 1110 0 . 11 000 1 ... 0 1111 0100 1101 mask=%mask_22_13
|
||||
VCMPEQ_fp_scalar 1111 1110 0 . 11 ... 1 ... 0 1111 0100 .... @vcmp_fp_scalar size=1
|
||||
VCMPEQ_scalar 1111 1110 0 . .. ... 1 ... 0 1111 0100 .... @vcmp_scalar
|
||||
}
|
||||
VCMPNE_scalar 1111 1110 0 . .. ... 1 ... 0 1111 1 1 0 0 .... @vcmp_scalar
|
||||
|
||||
{
|
||||
VCMPNE_fp_scalar 1111 1110 0 . 11 ... 1 ... 0 1111 1100 .... @vcmp_fp_scalar size=1
|
||||
VCMPNE_scalar 1111 1110 0 . .. ... 1 ... 0 1111 1100 .... @vcmp_scalar
|
||||
}
|
||||
|
||||
{
|
||||
VCMPGT_fp_scalar 1111 1110 0 . 11 ... 1 ... 1 1111 0110 .... @vcmp_fp_scalar size=1
|
||||
VCMPGT_scalar 1111 1110 0 . .. ... 1 ... 1 1111 0110 .... @vcmp_scalar
|
||||
}
|
||||
|
||||
{
|
||||
VCMPLE_fp_scalar 1111 1110 0 . 11 ... 1 ... 1 1111 1110 .... @vcmp_fp_scalar size=1
|
||||
VCMPLE_scalar 1111 1110 0 . .. ... 1 ... 1 1111 1110 .... @vcmp_scalar
|
||||
}
|
||||
|
||||
{
|
||||
VCMPGE_fp_scalar 1111 1110 0 . 11 ... 1 ... 1 1111 0100 .... @vcmp_fp_scalar size=1
|
||||
VCMPGE_scalar 1111 1110 0 . .. ... 1 ... 1 1111 0100 .... @vcmp_scalar
|
||||
}
|
||||
{
|
||||
VCMPLT_fp_scalar 1111 1110 0 . 11 ... 1 ... 1 1111 1100 .... @vcmp_fp_scalar size=1
|
||||
VCMPLT_scalar 1111 1110 0 . .. ... 1 ... 1 1111 1100 .... @vcmp_scalar
|
||||
}
|
||||
|
||||
VCMPCS_scalar 1111 1110 0 . .. ... 1 ... 0 1111 0 1 1 0 .... @vcmp_scalar
|
||||
VCMPHI_scalar 1111 1110 0 . .. ... 1 ... 0 1111 1 1 1 0 .... @vcmp_scalar
|
||||
VCMPGE_scalar 1111 1110 0 . .. ... 1 ... 1 1111 0 1 0 0 .... @vcmp_scalar
|
||||
VCMPLT_scalar 1111 1110 0 . .. ... 1 ... 1 1111 1 1 0 0 .... @vcmp_scalar
|
||||
VCMPGT_scalar 1111 1110 0 . .. ... 1 ... 1 1111 0 1 1 0 .... @vcmp_scalar
|
||||
VCMPLE_scalar 1111 1110 0 . .. ... 1 ... 1 1111 1 1 1 0 .... @vcmp_scalar
|
||||
|
||||
# 2-operand FP
|
||||
VADD_fp 1110 1111 0 . 0 . ... 0 ... 0 1101 . 1 . 0 ... 0 @2op_fp
|
||||
VSUB_fp 1110 1111 0 . 1 . ... 0 ... 0 1101 . 1 . 0 ... 0 @2op_fp
|
||||
VMUL_fp 1111 1111 0 . 0 . ... 0 ... 0 1101 . 1 . 1 ... 0 @2op_fp
|
||||
VABD_fp 1111 1111 0 . 1 . ... 0 ... 0 1101 . 1 . 0 ... 0 @2op_fp
|
||||
|
||||
VMAXNM 1111 1111 0 . 0 . ... 0 ... 0 1111 . 1 . 1 ... 0 @2op_fp
|
||||
VMINNM 1111 1111 0 . 1 . ... 0 ... 0 1111 . 1 . 1 ... 0 @2op_fp
|
||||
|
||||
VCADD90_fp 1111 1100 1 . 0 . ... 0 ... 0 1000 . 1 . 0 ... 0 @2op_fp_size_rev
|
||||
VCADD270_fp 1111 1101 1 . 0 . ... 0 ... 0 1000 . 1 . 0 ... 0 @2op_fp_size_rev
|
||||
|
||||
VFMA 1110 1111 0 . 0 . ... 0 ... 0 1100 . 1 . 1 ... 0 @2op_fp
|
||||
VFMS 1110 1111 0 . 1 . ... 0 ... 0 1100 . 1 . 1 ... 0 @2op_fp
|
||||
|
||||
VCMLA0 1111 110 00 . 1 . ... 0 ... 0 1000 . 1 . 0 ... 0 @2op_fp_size_rev
|
||||
VCMLA90 1111 110 01 . 1 . ... 0 ... 0 1000 . 1 . 0 ... 0 @2op_fp_size_rev
|
||||
VCMLA180 1111 110 10 . 1 . ... 0 ... 0 1000 . 1 . 0 ... 0 @2op_fp_size_rev
|
||||
VCMLA270 1111 110 11 . 1 . ... 0 ... 0 1000 . 1 . 0 ... 0 @2op_fp_size_rev
|
||||
|
||||
# floating-point <-> fixed-point conversions. Naming convention:
|
||||
# VCVT_<from><to>, S = signed int, U = unsigned int, H = halfprec, F = singleprec
|
||||
@vcvt .... .... .. 1 ..... .... .. 1 . .... .... &2shift \
|
||||
qd=%qd qm=%qm shift=%rshift_i5 size=2
|
||||
@vcvt_f16 .... .... .. 11 .... .... .. 0 . .... .... &2shift \
|
||||
qd=%qd qm=%qm shift=%rshift_i4 size=1
|
||||
|
||||
VCVT_SH_fixed 1110 1111 1 . ...... ... 0 11 . 0 01 . 1 ... 0 @vcvt_f16
|
||||
VCVT_UH_fixed 1111 1111 1 . ...... ... 0 11 . 0 01 . 1 ... 0 @vcvt_f16
|
||||
|
||||
VCVT_HS_fixed 1110 1111 1 . ...... ... 0 11 . 1 01 . 1 ... 0 @vcvt_f16
|
||||
VCVT_HU_fixed 1111 1111 1 . ...... ... 0 11 . 1 01 . 1 ... 0 @vcvt_f16
|
||||
|
||||
VCVT_SF_fixed 1110 1111 1 . ...... ... 0 11 . 0 01 . 1 ... 0 @vcvt
|
||||
VCVT_UF_fixed 1111 1111 1 . ...... ... 0 11 . 0 01 . 1 ... 0 @vcvt
|
||||
|
||||
VCVT_FS_fixed 1110 1111 1 . ...... ... 0 11 . 1 01 . 1 ... 0 @vcvt
|
||||
VCVT_FU_fixed 1111 1111 1 . ...... ... 0 11 . 1 01 . 1 ... 0 @vcvt
|
||||
|
||||
# VCVT between floating point and integer (halfprec and single);
|
||||
# VCVT_<from><to>, S = signed int, U = unsigned int, F = float
|
||||
VCVT_SF 1111 1111 1 . 11 .. 11 ... 0 011 00 1 . 0 ... 0 @1op
|
||||
VCVT_UF 1111 1111 1 . 11 .. 11 ... 0 011 01 1 . 0 ... 0 @1op
|
||||
VCVT_FS 1111 1111 1 . 11 .. 11 ... 0 011 10 1 . 0 ... 0 @1op
|
||||
VCVT_FU 1111 1111 1 . 11 .. 11 ... 0 011 11 1 . 0 ... 0 @1op
|
||||
|
||||
# VCVT from floating point to integer with specified rounding mode
|
||||
VCVTAS 1111 1111 1 . 11 .. 11 ... 000 00 0 1 . 0 ... 0 @1op
|
||||
VCVTAU 1111 1111 1 . 11 .. 11 ... 000 00 1 1 . 0 ... 0 @1op
|
||||
VCVTNS 1111 1111 1 . 11 .. 11 ... 000 01 0 1 . 0 ... 0 @1op
|
||||
VCVTNU 1111 1111 1 . 11 .. 11 ... 000 01 1 1 . 0 ... 0 @1op
|
||||
VCVTPS 1111 1111 1 . 11 .. 11 ... 000 10 0 1 . 0 ... 0 @1op
|
||||
VCVTPU 1111 1111 1 . 11 .. 11 ... 000 10 1 1 . 0 ... 0 @1op
|
||||
VCVTMS 1111 1111 1 . 11 .. 11 ... 000 11 0 1 . 0 ... 0 @1op
|
||||
VCVTMU 1111 1111 1 . 11 .. 11 ... 000 11 1 1 . 0 ... 0 @1op
|
||||
|
||||
VRINTN 1111 1111 1 . 11 .. 10 ... 001 000 1 . 0 ... 0 @1op
|
||||
VRINTX 1111 1111 1 . 11 .. 10 ... 001 001 1 . 0 ... 0 @1op
|
||||
VRINTA 1111 1111 1 . 11 .. 10 ... 001 010 1 . 0 ... 0 @1op
|
||||
VRINTZ 1111 1111 1 . 11 .. 10 ... 001 011 1 . 0 ... 0 @1op
|
||||
VRINTM 1111 1111 1 . 11 .. 10 ... 001 101 1 . 0 ... 0 @1op
|
||||
VRINTP 1111 1111 1 . 11 .. 10 ... 001 111 1 . 0 ... 0 @1op
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "exec/cpu_ldst.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "fpu/softfloat.h"
|
||||
|
||||
static uint16_t mve_eci_mask(CPUARMState *env)
|
||||
{
|
||||
|
@ -2798,3 +2799,652 @@ DO_VMAXMINA(vmaxaw, 4, int32_t, uint32_t, DO_MAX)
|
|||
DO_VMAXMINA(vminab, 1, int8_t, uint8_t, DO_MIN)
|
||||
DO_VMAXMINA(vminah, 2, int16_t, uint16_t, DO_MIN)
|
||||
DO_VMAXMINA(vminaw, 4, int32_t, uint32_t, DO_MIN)
|
||||
|
||||
/*
|
||||
* 2-operand floating point. Note that if an element is partially
|
||||
* predicated we must do the FP operation to update the non-predicated
|
||||
* bytes, but we must be careful to avoid updating the FP exception
|
||||
* state unless byte 0 of the element was unpredicated.
|
||||
*/
|
||||
#define DO_2OP_FP(OP, ESIZE, TYPE, FN) \
|
||||
void HELPER(glue(mve_, OP))(CPUARMState *env, \
|
||||
void *vd, void *vn, void *vm) \
|
||||
{ \
|
||||
TYPE *d = vd, *n = vn, *m = vm; \
|
||||
TYPE r; \
|
||||
uint16_t mask = mve_element_mask(env); \
|
||||
unsigned e; \
|
||||
float_status *fpst; \
|
||||
float_status scratch_fpst; \
|
||||
for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \
|
||||
if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \
|
||||
continue; \
|
||||
} \
|
||||
fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \
|
||||
&env->vfp.standard_fp_status; \
|
||||
if (!(mask & 1)) { \
|
||||
/* We need the result but without updating flags */ \
|
||||
scratch_fpst = *fpst; \
|
||||
fpst = &scratch_fpst; \
|
||||
} \
|
||||
r = FN(n[H##ESIZE(e)], m[H##ESIZE(e)], fpst); \
|
||||
mergemask(&d[H##ESIZE(e)], r, mask); \
|
||||
} \
|
||||
mve_advance_vpt(env); \
|
||||
}
|
||||
|
||||
#define DO_2OP_FP_ALL(OP, FN) \
|
||||
DO_2OP_FP(OP##h, 2, float16, float16_##FN) \
|
||||
DO_2OP_FP(OP##s, 4, float32, float32_##FN)
|
||||
|
||||
DO_2OP_FP_ALL(vfadd, add)
|
||||
DO_2OP_FP_ALL(vfsub, sub)
|
||||
DO_2OP_FP_ALL(vfmul, mul)
|
||||
|
||||
static inline float16 float16_abd(float16 a, float16 b, float_status *s)
|
||||
{
|
||||
return float16_abs(float16_sub(a, b, s));
|
||||
}
|
||||
|
||||
static inline float32 float32_abd(float32 a, float32 b, float_status *s)
|
||||
{
|
||||
return float32_abs(float32_sub(a, b, s));
|
||||
}
|
||||
|
||||
DO_2OP_FP_ALL(vfabd, abd)
|
||||
DO_2OP_FP_ALL(vmaxnm, maxnum)
|
||||
DO_2OP_FP_ALL(vminnm, minnum)
|
||||
|
||||
static inline float16 float16_maxnuma(float16 a, float16 b, float_status *s)
|
||||
{
|
||||
return float16_maxnum(float16_abs(a), float16_abs(b), s);
|
||||
}
|
||||
|
||||
static inline float32 float32_maxnuma(float32 a, float32 b, float_status *s)
|
||||
{
|
||||
return float32_maxnum(float32_abs(a), float32_abs(b), s);
|
||||
}
|
||||
|
||||
static inline float16 float16_minnuma(float16 a, float16 b, float_status *s)
|
||||
{
|
||||
return float16_minnum(float16_abs(a), float16_abs(b), s);
|
||||
}
|
||||
|
||||
static inline float32 float32_minnuma(float32 a, float32 b, float_status *s)
|
||||
{
|
||||
return float32_minnum(float32_abs(a), float32_abs(b), s);
|
||||
}
|
||||
|
||||
DO_2OP_FP_ALL(vmaxnma, maxnuma)
|
||||
DO_2OP_FP_ALL(vminnma, minnuma)
|
||||
|
||||
#define DO_VCADD_FP(OP, ESIZE, TYPE, FN0, FN1) \
|
||||
void HELPER(glue(mve_, OP))(CPUARMState *env, \
|
||||
void *vd, void *vn, void *vm) \
|
||||
{ \
|
||||
TYPE *d = vd, *n = vn, *m = vm; \
|
||||
TYPE r[16 / ESIZE]; \
|
||||
uint16_t tm, mask = mve_element_mask(env); \
|
||||
unsigned e; \
|
||||
float_status *fpst; \
|
||||
float_status scratch_fpst; \
|
||||
/* Calculate all results first to avoid overwriting inputs */ \
|
||||
for (e = 0, tm = mask; e < 16 / ESIZE; e++, tm >>= ESIZE) { \
|
||||
if ((tm & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \
|
||||
r[e] = 0; \
|
||||
continue; \
|
||||
} \
|
||||
fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \
|
||||
&env->vfp.standard_fp_status; \
|
||||
if (!(tm & 1)) { \
|
||||
/* We need the result but without updating flags */ \
|
||||
scratch_fpst = *fpst; \
|
||||
fpst = &scratch_fpst; \
|
||||
} \
|
||||
if (!(e & 1)) { \
|
||||
r[e] = FN0(n[H##ESIZE(e)], m[H##ESIZE(e + 1)], fpst); \
|
||||
} else { \
|
||||
r[e] = FN1(n[H##ESIZE(e)], m[H##ESIZE(e - 1)], fpst); \
|
||||
} \
|
||||
} \
|
||||
for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \
|
||||
mergemask(&d[H##ESIZE(e)], r[e], mask); \
|
||||
} \
|
||||
mve_advance_vpt(env); \
|
||||
}
|
||||
|
||||
DO_VCADD_FP(vfcadd90h, 2, float16, float16_sub, float16_add)
|
||||
DO_VCADD_FP(vfcadd90s, 4, float32, float32_sub, float32_add)
|
||||
DO_VCADD_FP(vfcadd270h, 2, float16, float16_add, float16_sub)
|
||||
DO_VCADD_FP(vfcadd270s, 4, float32, float32_add, float32_sub)
|
||||
|
||||
#define DO_VFMA(OP, ESIZE, TYPE, CHS) \
|
||||
void HELPER(glue(mve_, OP))(CPUARMState *env, \
|
||||
void *vd, void *vn, void *vm) \
|
||||
{ \
|
||||
TYPE *d = vd, *n = vn, *m = vm; \
|
||||
TYPE r; \
|
||||
uint16_t mask = mve_element_mask(env); \
|
||||
unsigned e; \
|
||||
float_status *fpst; \
|
||||
float_status scratch_fpst; \
|
||||
for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \
|
||||
if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \
|
||||
continue; \
|
||||
} \
|
||||
fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \
|
||||
&env->vfp.standard_fp_status; \
|
||||
if (!(mask & 1)) { \
|
||||
/* We need the result but without updating flags */ \
|
||||
scratch_fpst = *fpst; \
|
||||
fpst = &scratch_fpst; \
|
||||
} \
|
||||
r = n[H##ESIZE(e)]; \
|
||||
if (CHS) { \
|
||||
r = TYPE##_chs(r); \
|
||||
} \
|
||||
r = TYPE##_muladd(r, m[H##ESIZE(e)], d[H##ESIZE(e)], \
|
||||
0, fpst); \
|
||||
mergemask(&d[H##ESIZE(e)], r, mask); \
|
||||
} \
|
||||
mve_advance_vpt(env); \
|
||||
}
|
||||
|
||||
DO_VFMA(vfmah, 2, float16, false)
|
||||
DO_VFMA(vfmas, 4, float32, false)
|
||||
DO_VFMA(vfmsh, 2, float16, true)
|
||||
DO_VFMA(vfmss, 4, float32, true)
|
||||
|
||||
#define DO_VCMLA(OP, ESIZE, TYPE, ROT, FN) \
|
||||
void HELPER(glue(mve_, OP))(CPUARMState *env, \
|
||||
void *vd, void *vn, void *vm) \
|
||||
{ \
|
||||
TYPE *d = vd, *n = vn, *m = vm; \
|
||||
TYPE r0, r1, e1, e2, e3, e4; \
|
||||
uint16_t mask = mve_element_mask(env); \
|
||||
unsigned e; \
|
||||
float_status *fpst0, *fpst1; \
|
||||
float_status scratch_fpst; \
|
||||
/* We loop through pairs of elements at a time */ \
|
||||
for (e = 0; e < 16 / ESIZE; e += 2, mask >>= ESIZE * 2) { \
|
||||
if ((mask & MAKE_64BIT_MASK(0, ESIZE * 2)) == 0) { \
|
||||
continue; \
|
||||
} \
|
||||
fpst0 = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \
|
||||
&env->vfp.standard_fp_status; \
|
||||
fpst1 = fpst0; \
|
||||
if (!(mask & 1)) { \
|
||||
scratch_fpst = *fpst0; \
|
||||
fpst0 = &scratch_fpst; \
|
||||
} \
|
||||
if (!(mask & (1 << ESIZE))) { \
|
||||
scratch_fpst = *fpst1; \
|
||||
fpst1 = &scratch_fpst; \
|
||||
} \
|
||||
switch (ROT) { \
|
||||
case 0: \
|
||||
e1 = m[H##ESIZE(e)]; \
|
||||
e2 = n[H##ESIZE(e)]; \
|
||||
e3 = m[H##ESIZE(e + 1)]; \
|
||||
e4 = n[H##ESIZE(e)]; \
|
||||
break; \
|
||||
case 1: \
|
||||
e1 = TYPE##_chs(m[H##ESIZE(e + 1)]); \
|
||||
e2 = n[H##ESIZE(e + 1)]; \
|
||||
e3 = m[H##ESIZE(e)]; \
|
||||
e4 = n[H##ESIZE(e + 1)]; \
|
||||
break; \
|
||||
case 2: \
|
||||
e1 = TYPE##_chs(m[H##ESIZE(e)]); \
|
||||
e2 = n[H##ESIZE(e)]; \
|
||||
e3 = TYPE##_chs(m[H##ESIZE(e + 1)]); \
|
||||
e4 = n[H##ESIZE(e)]; \
|
||||
break; \
|
||||
case 3: \
|
||||
e1 = m[H##ESIZE(e + 1)]; \
|
||||
e2 = n[H##ESIZE(e + 1)]; \
|
||||
e3 = TYPE##_chs(m[H##ESIZE(e)]); \
|
||||
e4 = n[H##ESIZE(e + 1)]; \
|
||||
break; \
|
||||
default: \
|
||||
g_assert_not_reached(); \
|
||||
} \
|
||||
r0 = FN(e2, e1, d[H##ESIZE(e)], fpst0); \
|
||||
r1 = FN(e4, e3, d[H##ESIZE(e + 1)], fpst1); \
|
||||
mergemask(&d[H##ESIZE(e)], r0, mask); \
|
||||
mergemask(&d[H##ESIZE(e + 1)], r1, mask >> ESIZE); \
|
||||
} \
|
||||
mve_advance_vpt(env); \
|
||||
}
|
||||
|
||||
#define DO_VCMULH(N, M, D, S) float16_mul(N, M, S)
|
||||
#define DO_VCMULS(N, M, D, S) float32_mul(N, M, S)
|
||||
|
||||
#define DO_VCMLAH(N, M, D, S) float16_muladd(N, M, D, 0, S)
|
||||
#define DO_VCMLAS(N, M, D, S) float32_muladd(N, M, D, 0, S)
|
||||
|
||||
DO_VCMLA(vcmul0h, 2, float16, 0, DO_VCMULH)
|
||||
DO_VCMLA(vcmul0s, 4, float32, 0, DO_VCMULS)
|
||||
DO_VCMLA(vcmul90h, 2, float16, 1, DO_VCMULH)
|
||||
DO_VCMLA(vcmul90s, 4, float32, 1, DO_VCMULS)
|
||||
DO_VCMLA(vcmul180h, 2, float16, 2, DO_VCMULH)
|
||||
DO_VCMLA(vcmul180s, 4, float32, 2, DO_VCMULS)
|
||||
DO_VCMLA(vcmul270h, 2, float16, 3, DO_VCMULH)
|
||||
DO_VCMLA(vcmul270s, 4, float32, 3, DO_VCMULS)
|
||||
|
||||
DO_VCMLA(vcmla0h, 2, float16, 0, DO_VCMLAH)
|
||||
DO_VCMLA(vcmla0s, 4, float32, 0, DO_VCMLAS)
|
||||
DO_VCMLA(vcmla90h, 2, float16, 1, DO_VCMLAH)
|
||||
DO_VCMLA(vcmla90s, 4, float32, 1, DO_VCMLAS)
|
||||
DO_VCMLA(vcmla180h, 2, float16, 2, DO_VCMLAH)
|
||||
DO_VCMLA(vcmla180s, 4, float32, 2, DO_VCMLAS)
|
||||
DO_VCMLA(vcmla270h, 2, float16, 3, DO_VCMLAH)
|
||||
DO_VCMLA(vcmla270s, 4, float32, 3, DO_VCMLAS)
|
||||
|
||||
#define DO_2OP_FP_SCALAR(OP, ESIZE, TYPE, FN) \
|
||||
void HELPER(glue(mve_, OP))(CPUARMState *env, \
|
||||
void *vd, void *vn, uint32_t rm) \
|
||||
{ \
|
||||
TYPE *d = vd, *n = vn; \
|
||||
TYPE r, m = rm; \
|
||||
uint16_t mask = mve_element_mask(env); \
|
||||
unsigned e; \
|
||||
float_status *fpst; \
|
||||
float_status scratch_fpst; \
|
||||
for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \
|
||||
if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \
|
||||
continue; \
|
||||
} \
|
||||
fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \
|
||||
&env->vfp.standard_fp_status; \
|
||||
if (!(mask & 1)) { \
|
||||
/* We need the result but without updating flags */ \
|
||||
scratch_fpst = *fpst; \
|
||||
fpst = &scratch_fpst; \
|
||||
} \
|
||||
r = FN(n[H##ESIZE(e)], m, fpst); \
|
||||
mergemask(&d[H##ESIZE(e)], r, mask); \
|
||||
} \
|
||||
mve_advance_vpt(env); \
|
||||
}
|
||||
|
||||
#define DO_2OP_FP_SCALAR_ALL(OP, FN) \
|
||||
DO_2OP_FP_SCALAR(OP##h, 2, float16, float16_##FN) \
|
||||
DO_2OP_FP_SCALAR(OP##s, 4, float32, float32_##FN)
|
||||
|
||||
DO_2OP_FP_SCALAR_ALL(vfadd_scalar, add)
|
||||
DO_2OP_FP_SCALAR_ALL(vfsub_scalar, sub)
|
||||
DO_2OP_FP_SCALAR_ALL(vfmul_scalar, mul)
|
||||
|
||||
#define DO_2OP_FP_ACC_SCALAR(OP, ESIZE, TYPE, FN) \
|
||||
void HELPER(glue(mve_, OP))(CPUARMState *env, \
|
||||
void *vd, void *vn, uint32_t rm) \
|
||||
{ \
|
||||
TYPE *d = vd, *n = vn; \
|
||||
TYPE r, m = rm; \
|
||||
uint16_t mask = mve_element_mask(env); \
|
||||
unsigned e; \
|
||||
float_status *fpst; \
|
||||
float_status scratch_fpst; \
|
||||
for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \
|
||||
if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \
|
||||
continue; \
|
||||
} \
|
||||
fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \
|
||||
&env->vfp.standard_fp_status; \
|
||||
if (!(mask & 1)) { \
|
||||
/* We need the result but without updating flags */ \
|
||||
scratch_fpst = *fpst; \
|
||||
fpst = &scratch_fpst; \
|
||||
} \
|
||||
r = FN(n[H##ESIZE(e)], m, d[H##ESIZE(e)], 0, fpst); \
|
||||
mergemask(&d[H##ESIZE(e)], r, mask); \
|
||||
} \
|
||||
mve_advance_vpt(env); \
|
||||
}
|
||||
|
||||
/* VFMAS is vector * vector + scalar, so swap op2 and op3 */
|
||||
#define DO_VFMAS_SCALARH(N, M, D, F, S) float16_muladd(N, D, M, F, S)
|
||||
#define DO_VFMAS_SCALARS(N, M, D, F, S) float32_muladd(N, D, M, F, S)
|
||||
|
||||
/* VFMA is vector * scalar + vector */
|
||||
DO_2OP_FP_ACC_SCALAR(vfma_scalarh, 2, float16, float16_muladd)
|
||||
DO_2OP_FP_ACC_SCALAR(vfma_scalars, 4, float32, float32_muladd)
|
||||
DO_2OP_FP_ACC_SCALAR(vfmas_scalarh, 2, float16, DO_VFMAS_SCALARH)
|
||||
DO_2OP_FP_ACC_SCALAR(vfmas_scalars, 4, float32, DO_VFMAS_SCALARS)
|
||||
|
||||
/* Floating point max/min across vector. */
|
||||
#define DO_FP_VMAXMINV(OP, ESIZE, TYPE, ABS, FN) \
|
||||
uint32_t HELPER(glue(mve_, OP))(CPUARMState *env, void *vm, \
|
||||
uint32_t ra_in) \
|
||||
{ \
|
||||
uint16_t mask = mve_element_mask(env); \
|
||||
unsigned e; \
|
||||
TYPE *m = vm; \
|
||||
TYPE ra = (TYPE)ra_in; \
|
||||
float_status *fpst = (ESIZE == 2) ? \
|
||||
&env->vfp.standard_fp_status_f16 : \
|
||||
&env->vfp.standard_fp_status; \
|
||||
for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \
|
||||
if (mask & 1) { \
|
||||
TYPE v = m[H##ESIZE(e)]; \
|
||||
if (TYPE##_is_signaling_nan(ra, fpst)) { \
|
||||
ra = TYPE##_silence_nan(ra, fpst); \
|
||||
float_raise(float_flag_invalid, fpst); \
|
||||
} \
|
||||
if (TYPE##_is_signaling_nan(v, fpst)) { \
|
||||
v = TYPE##_silence_nan(v, fpst); \
|
||||
float_raise(float_flag_invalid, fpst); \
|
||||
} \
|
||||
if (ABS) { \
|
||||
v = TYPE##_abs(v); \
|
||||
} \
|
||||
ra = FN(ra, v, fpst); \
|
||||
} \
|
||||
} \
|
||||
mve_advance_vpt(env); \
|
||||
return ra; \
|
||||
} \
|
||||
|
||||
#define NOP(X) (X)
|
||||
|
||||
DO_FP_VMAXMINV(vmaxnmvh, 2, float16, false, float16_maxnum)
|
||||
DO_FP_VMAXMINV(vmaxnmvs, 4, float32, false, float32_maxnum)
|
||||
DO_FP_VMAXMINV(vminnmvh, 2, float16, false, float16_minnum)
|
||||
DO_FP_VMAXMINV(vminnmvs, 4, float32, false, float32_minnum)
|
||||
DO_FP_VMAXMINV(vmaxnmavh, 2, float16, true, float16_maxnum)
|
||||
DO_FP_VMAXMINV(vmaxnmavs, 4, float32, true, float32_maxnum)
|
||||
DO_FP_VMAXMINV(vminnmavh, 2, float16, true, float16_minnum)
|
||||
DO_FP_VMAXMINV(vminnmavs, 4, float32, true, float32_minnum)
|
||||
|
||||
/* FP compares; note that all comparisons signal InvalidOp for QNaNs */
|
||||
#define DO_VCMP_FP(OP, ESIZE, TYPE, FN) \
|
||||
void HELPER(glue(mve_, OP))(CPUARMState *env, void *vn, void *vm) \
|
||||
{ \
|
||||
TYPE *n = vn, *m = vm; \
|
||||
uint16_t mask = mve_element_mask(env); \
|
||||
uint16_t eci_mask = mve_eci_mask(env); \
|
||||
uint16_t beatpred = 0; \
|
||||
uint16_t emask = MAKE_64BIT_MASK(0, ESIZE); \
|
||||
unsigned e; \
|
||||
float_status *fpst; \
|
||||
float_status scratch_fpst; \
|
||||
bool r; \
|
||||
for (e = 0; e < 16 / ESIZE; e++, emask <<= ESIZE) { \
|
||||
if ((mask & emask) == 0) { \
|
||||
continue; \
|
||||
} \
|
||||
fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \
|
||||
&env->vfp.standard_fp_status; \
|
||||
if (!(mask & (1 << (e * ESIZE)))) { \
|
||||
/* We need the result but without updating flags */ \
|
||||
scratch_fpst = *fpst; \
|
||||
fpst = &scratch_fpst; \
|
||||
} \
|
||||
r = FN(n[H##ESIZE(e)], m[H##ESIZE(e)], fpst); \
|
||||
/* Comparison sets 0/1 bits for each byte in the element */ \
|
||||
beatpred |= r * emask; \
|
||||
} \
|
||||
beatpred &= mask; \
|
||||
env->v7m.vpr = (env->v7m.vpr & ~(uint32_t)eci_mask) | \
|
||||
(beatpred & eci_mask); \
|
||||
mve_advance_vpt(env); \
|
||||
}
|
||||
|
||||
#define DO_VCMP_FP_SCALAR(OP, ESIZE, TYPE, FN) \
|
||||
void HELPER(glue(mve_, OP))(CPUARMState *env, void *vn, \
|
||||
uint32_t rm) \
|
||||
{ \
|
||||
TYPE *n = vn; \
|
||||
uint16_t mask = mve_element_mask(env); \
|
||||
uint16_t eci_mask = mve_eci_mask(env); \
|
||||
uint16_t beatpred = 0; \
|
||||
uint16_t emask = MAKE_64BIT_MASK(0, ESIZE); \
|
||||
unsigned e; \
|
||||
float_status *fpst; \
|
||||
float_status scratch_fpst; \
|
||||
bool r; \
|
||||
for (e = 0; e < 16 / ESIZE; e++, emask <<= ESIZE) { \
|
||||
if ((mask & emask) == 0) { \
|
||||
continue; \
|
||||
} \
|
||||
fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \
|
||||
&env->vfp.standard_fp_status; \
|
||||
if (!(mask & (1 << (e * ESIZE)))) { \
|
||||
/* We need the result but without updating flags */ \
|
||||
scratch_fpst = *fpst; \
|
||||
fpst = &scratch_fpst; \
|
||||
} \
|
||||
r = FN(n[H##ESIZE(e)], (TYPE)rm, fpst); \
|
||||
/* Comparison sets 0/1 bits for each byte in the element */ \
|
||||
beatpred |= r * emask; \
|
||||
} \
|
||||
beatpred &= mask; \
|
||||
env->v7m.vpr = (env->v7m.vpr & ~(uint32_t)eci_mask) | \
|
||||
(beatpred & eci_mask); \
|
||||
mve_advance_vpt(env); \
|
||||
}
|
||||
|
||||
#define DO_VCMP_FP_BOTH(VOP, SOP, ESIZE, TYPE, FN) \
|
||||
DO_VCMP_FP(VOP, ESIZE, TYPE, FN) \
|
||||
DO_VCMP_FP_SCALAR(SOP, ESIZE, TYPE, FN)
|
||||
|
||||
/*
|
||||
* Some care is needed here to get the correct result for the unordered case.
|
||||
* Architecturally EQ, GE and GT are defined to be false for unordered, but
|
||||
* the NE, LT and LE comparisons are defined as simple logical inverses of
|
||||
* EQ, GE and GT and so they must return true for unordered. The softfloat
|
||||
* comparison functions float*_{eq,le,lt} all return false for unordered.
|
||||
*/
|
||||
#define DO_GE16(X, Y, S) float16_le(Y, X, S)
|
||||
#define DO_GE32(X, Y, S) float32_le(Y, X, S)
|
||||
#define DO_GT16(X, Y, S) float16_lt(Y, X, S)
|
||||
#define DO_GT32(X, Y, S) float32_lt(Y, X, S)
|
||||
|
||||
DO_VCMP_FP_BOTH(vfcmpeqh, vfcmpeq_scalarh, 2, float16, float16_eq)
|
||||
DO_VCMP_FP_BOTH(vfcmpeqs, vfcmpeq_scalars, 4, float32, float32_eq)
|
||||
|
||||
DO_VCMP_FP_BOTH(vfcmpneh, vfcmpne_scalarh, 2, float16, !float16_eq)
|
||||
DO_VCMP_FP_BOTH(vfcmpnes, vfcmpne_scalars, 4, float32, !float32_eq)
|
||||
|
||||
DO_VCMP_FP_BOTH(vfcmpgeh, vfcmpge_scalarh, 2, float16, DO_GE16)
|
||||
DO_VCMP_FP_BOTH(vfcmpges, vfcmpge_scalars, 4, float32, DO_GE32)
|
||||
|
||||
DO_VCMP_FP_BOTH(vfcmplth, vfcmplt_scalarh, 2, float16, !DO_GE16)
|
||||
DO_VCMP_FP_BOTH(vfcmplts, vfcmplt_scalars, 4, float32, !DO_GE32)
|
||||
|
||||
DO_VCMP_FP_BOTH(vfcmpgth, vfcmpgt_scalarh, 2, float16, DO_GT16)
|
||||
DO_VCMP_FP_BOTH(vfcmpgts, vfcmpgt_scalars, 4, float32, DO_GT32)
|
||||
|
||||
DO_VCMP_FP_BOTH(vfcmpleh, vfcmple_scalarh, 2, float16, !DO_GT16)
|
||||
DO_VCMP_FP_BOTH(vfcmples, vfcmple_scalars, 4, float32, !DO_GT32)
|
||||
|
||||
#define DO_VCVT_FIXED(OP, ESIZE, TYPE, FN) \
|
||||
void HELPER(glue(mve_, OP))(CPUARMState *env, void *vd, void *vm, \
|
||||
uint32_t shift) \
|
||||
{ \
|
||||
TYPE *d = vd, *m = vm; \
|
||||
TYPE r; \
|
||||
uint16_t mask = mve_element_mask(env); \
|
||||
unsigned e; \
|
||||
float_status *fpst; \
|
||||
float_status scratch_fpst; \
|
||||
for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \
|
||||
if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \
|
||||
continue; \
|
||||
} \
|
||||
fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \
|
||||
&env->vfp.standard_fp_status; \
|
||||
if (!(mask & 1)) { \
|
||||
/* We need the result but without updating flags */ \
|
||||
scratch_fpst = *fpst; \
|
||||
fpst = &scratch_fpst; \
|
||||
} \
|
||||
r = FN(m[H##ESIZE(e)], shift, fpst); \
|
||||
mergemask(&d[H##ESIZE(e)], r, mask); \
|
||||
} \
|
||||
mve_advance_vpt(env); \
|
||||
}
|
||||
|
||||
DO_VCVT_FIXED(vcvt_sh, 2, int16_t, helper_vfp_shtoh)
|
||||
DO_VCVT_FIXED(vcvt_uh, 2, uint16_t, helper_vfp_uhtoh)
|
||||
DO_VCVT_FIXED(vcvt_hs, 2, int16_t, helper_vfp_toshh_round_to_zero)
|
||||
DO_VCVT_FIXED(vcvt_hu, 2, uint16_t, helper_vfp_touhh_round_to_zero)
|
||||
DO_VCVT_FIXED(vcvt_sf, 4, int32_t, helper_vfp_sltos)
|
||||
DO_VCVT_FIXED(vcvt_uf, 4, uint32_t, helper_vfp_ultos)
|
||||
DO_VCVT_FIXED(vcvt_fs, 4, int32_t, helper_vfp_tosls_round_to_zero)
|
||||
DO_VCVT_FIXED(vcvt_fu, 4, uint32_t, helper_vfp_touls_round_to_zero)
|
||||
|
||||
/* VCVT with specified rmode */
|
||||
#define DO_VCVT_RMODE(OP, ESIZE, TYPE, FN) \
|
||||
void HELPER(glue(mve_, OP))(CPUARMState *env, \
|
||||
void *vd, void *vm, uint32_t rmode) \
|
||||
{ \
|
||||
TYPE *d = vd, *m = vm; \
|
||||
TYPE r; \
|
||||
uint16_t mask = mve_element_mask(env); \
|
||||
unsigned e; \
|
||||
float_status *fpst; \
|
||||
float_status scratch_fpst; \
|
||||
float_status *base_fpst = (ESIZE == 2) ? \
|
||||
&env->vfp.standard_fp_status_f16 : \
|
||||
&env->vfp.standard_fp_status; \
|
||||
uint32_t prev_rmode = get_float_rounding_mode(base_fpst); \
|
||||
set_float_rounding_mode(rmode, base_fpst); \
|
||||
for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \
|
||||
if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \
|
||||
continue; \
|
||||
} \
|
||||
fpst = base_fpst; \
|
||||
if (!(mask & 1)) { \
|
||||
/* We need the result but without updating flags */ \
|
||||
scratch_fpst = *fpst; \
|
||||
fpst = &scratch_fpst; \
|
||||
} \
|
||||
r = FN(m[H##ESIZE(e)], 0, fpst); \
|
||||
mergemask(&d[H##ESIZE(e)], r, mask); \
|
||||
} \
|
||||
set_float_rounding_mode(prev_rmode, base_fpst); \
|
||||
mve_advance_vpt(env); \
|
||||
}
|
||||
|
||||
DO_VCVT_RMODE(vcvt_rm_sh, 2, uint16_t, helper_vfp_toshh)
|
||||
DO_VCVT_RMODE(vcvt_rm_uh, 2, uint16_t, helper_vfp_touhh)
|
||||
DO_VCVT_RMODE(vcvt_rm_ss, 4, uint32_t, helper_vfp_tosls)
|
||||
DO_VCVT_RMODE(vcvt_rm_us, 4, uint32_t, helper_vfp_touls)
|
||||
|
||||
#define DO_VRINT_RM_H(M, F, S) helper_rinth(M, S)
|
||||
#define DO_VRINT_RM_S(M, F, S) helper_rints(M, S)
|
||||
|
||||
DO_VCVT_RMODE(vrint_rm_h, 2, uint16_t, DO_VRINT_RM_H)
|
||||
DO_VCVT_RMODE(vrint_rm_s, 4, uint32_t, DO_VRINT_RM_S)
|
||||
|
||||
/*
|
||||
* VCVT between halfprec and singleprec. As usual for halfprec
|
||||
* conversions, FZ16 is ignored and AHP is observed.
|
||||
*/
|
||||
static void do_vcvt_sh(CPUARMState *env, void *vd, void *vm, int top)
|
||||
{
|
||||
uint16_t *d = vd;
|
||||
uint32_t *m = vm;
|
||||
uint16_t r;
|
||||
uint16_t mask = mve_element_mask(env);
|
||||
bool ieee = !(env->vfp.xregs[ARM_VFP_FPSCR] & FPCR_AHP);
|
||||
unsigned e;
|
||||
float_status *fpst;
|
||||
float_status scratch_fpst;
|
||||
float_status *base_fpst = &env->vfp.standard_fp_status;
|
||||
bool old_fz = get_flush_to_zero(base_fpst);
|
||||
set_flush_to_zero(false, base_fpst);
|
||||
for (e = 0; e < 16 / 4; e++, mask >>= 4) {
|
||||
if ((mask & MAKE_64BIT_MASK(0, 4)) == 0) {
|
||||
continue;
|
||||
}
|
||||
fpst = base_fpst;
|
||||
if (!(mask & 1)) {
|
||||
/* We need the result but without updating flags */
|
||||
scratch_fpst = *fpst;
|
||||
fpst = &scratch_fpst;
|
||||
}
|
||||
r = float32_to_float16(m[H4(e)], ieee, fpst);
|
||||
mergemask(&d[H2(e * 2 + top)], r, mask >> (top * 2));
|
||||
}
|
||||
set_flush_to_zero(old_fz, base_fpst);
|
||||
mve_advance_vpt(env);
|
||||
}
|
||||
|
||||
static void do_vcvt_hs(CPUARMState *env, void *vd, void *vm, int top)
|
||||
{
|
||||
uint32_t *d = vd;
|
||||
uint16_t *m = vm;
|
||||
uint32_t r;
|
||||
uint16_t mask = mve_element_mask(env);
|
||||
bool ieee = !(env->vfp.xregs[ARM_VFP_FPSCR] & FPCR_AHP);
|
||||
unsigned e;
|
||||
float_status *fpst;
|
||||
float_status scratch_fpst;
|
||||
float_status *base_fpst = &env->vfp.standard_fp_status;
|
||||
bool old_fiz = get_flush_inputs_to_zero(base_fpst);
|
||||
set_flush_inputs_to_zero(false, base_fpst);
|
||||
for (e = 0; e < 16 / 4; e++, mask >>= 4) {
|
||||
if ((mask & MAKE_64BIT_MASK(0, 4)) == 0) {
|
||||
continue;
|
||||
}
|
||||
fpst = base_fpst;
|
||||
if (!(mask & (1 << (top * 2)))) {
|
||||
/* We need the result but without updating flags */
|
||||
scratch_fpst = *fpst;
|
||||
fpst = &scratch_fpst;
|
||||
}
|
||||
r = float16_to_float32(m[H2(e * 2 + top)], ieee, fpst);
|
||||
mergemask(&d[H4(e)], r, mask);
|
||||
}
|
||||
set_flush_inputs_to_zero(old_fiz, base_fpst);
|
||||
mve_advance_vpt(env);
|
||||
}
|
||||
|
||||
void HELPER(mve_vcvtb_sh)(CPUARMState *env, void *vd, void *vm)
|
||||
{
|
||||
do_vcvt_sh(env, vd, vm, 0);
|
||||
}
|
||||
void HELPER(mve_vcvtt_sh)(CPUARMState *env, void *vd, void *vm)
|
||||
{
|
||||
do_vcvt_sh(env, vd, vm, 1);
|
||||
}
|
||||
void HELPER(mve_vcvtb_hs)(CPUARMState *env, void *vd, void *vm)
|
||||
{
|
||||
do_vcvt_hs(env, vd, vm, 0);
|
||||
}
|
||||
void HELPER(mve_vcvtt_hs)(CPUARMState *env, void *vd, void *vm)
|
||||
{
|
||||
do_vcvt_hs(env, vd, vm, 1);
|
||||
}
|
||||
|
||||
#define DO_1OP_FP(OP, ESIZE, TYPE, FN) \
|
||||
void HELPER(glue(mve_, OP))(CPUARMState *env, void *vd, void *vm) \
|
||||
{ \
|
||||
TYPE *d = vd, *m = vm; \
|
||||
TYPE r; \
|
||||
uint16_t mask = mve_element_mask(env); \
|
||||
unsigned e; \
|
||||
float_status *fpst; \
|
||||
float_status scratch_fpst; \
|
||||
for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \
|
||||
if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \
|
||||
continue; \
|
||||
} \
|
||||
fpst = (ESIZE == 2) ? &env->vfp.standard_fp_status_f16 : \
|
||||
&env->vfp.standard_fp_status; \
|
||||
if (!(mask & 1)) { \
|
||||
/* We need the result but without updating flags */ \
|
||||
scratch_fpst = *fpst; \
|
||||
fpst = &scratch_fpst; \
|
||||
} \
|
||||
r = FN(m[H##ESIZE(e)], fpst); \
|
||||
mergemask(&d[H##ESIZE(e)], r, mask); \
|
||||
} \
|
||||
mve_advance_vpt(env); \
|
||||
}
|
||||
|
||||
DO_1OP_FP(vrintx_h, 2, float16, float16_round_to_int)
|
||||
DO_1OP_FP(vrintx_s, 4, float32, float32_round_to_int)
|
||||
|
|
|
@ -49,6 +49,7 @@ typedef void MVEGenCmpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr);
|
|||
typedef void MVEGenScalarCmpFn(TCGv_ptr, TCGv_ptr, TCGv_i32);
|
||||
typedef void MVEGenVABAVFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32);
|
||||
typedef void MVEGenDualAccOpFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32);
|
||||
typedef void MVEGenVCVTRmodeFn(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32);
|
||||
|
||||
/* Return the offset of a Qn register (same semantics as aa32_vfp_qreg()) */
|
||||
static inline long mve_qreg_offset(unsigned reg)
|
||||
|
@ -543,6 +544,148 @@ DO_1OP(VQNEG, vqneg)
|
|||
DO_1OP(VMAXA, vmaxa)
|
||||
DO_1OP(VMINA, vmina)
|
||||
|
||||
/*
|
||||
* For simple float/int conversions we use the fixed-point
|
||||
* conversion helpers with a zero shift count
|
||||
*/
|
||||
#define DO_VCVT(INSN, HFN, SFN) \
|
||||
static void gen_##INSN##h(TCGv_ptr env, TCGv_ptr qd, TCGv_ptr qm) \
|
||||
{ \
|
||||
gen_helper_mve_##HFN(env, qd, qm, tcg_constant_i32(0)); \
|
||||
} \
|
||||
static void gen_##INSN##s(TCGv_ptr env, TCGv_ptr qd, TCGv_ptr qm) \
|
||||
{ \
|
||||
gen_helper_mve_##SFN(env, qd, qm, tcg_constant_i32(0)); \
|
||||
} \
|
||||
static bool trans_##INSN(DisasContext *s, arg_1op *a) \
|
||||
{ \
|
||||
static MVEGenOneOpFn * const fns[] = { \
|
||||
NULL, \
|
||||
gen_##INSN##h, \
|
||||
gen_##INSN##s, \
|
||||
NULL, \
|
||||
}; \
|
||||
if (!dc_isar_feature(aa32_mve_fp, s)) { \
|
||||
return false; \
|
||||
} \
|
||||
return do_1op(s, a, fns[a->size]); \
|
||||
}
|
||||
|
||||
DO_VCVT(VCVT_SF, vcvt_sh, vcvt_sf)
|
||||
DO_VCVT(VCVT_UF, vcvt_uh, vcvt_uf)
|
||||
DO_VCVT(VCVT_FS, vcvt_hs, vcvt_fs)
|
||||
DO_VCVT(VCVT_FU, vcvt_hu, vcvt_fu)
|
||||
|
||||
static bool do_vcvt_rmode(DisasContext *s, arg_1op *a,
|
||||
enum arm_fprounding rmode, bool u)
|
||||
{
|
||||
/*
|
||||
* Handle VCVT fp to int with specified rounding mode.
|
||||
* This is a 1op fn but we must pass the rounding mode as
|
||||
* an immediate to the helper.
|
||||
*/
|
||||
TCGv_ptr qd, qm;
|
||||
static MVEGenVCVTRmodeFn * const fns[4][2] = {
|
||||
{ NULL, NULL },
|
||||
{ gen_helper_mve_vcvt_rm_sh, gen_helper_mve_vcvt_rm_uh },
|
||||
{ gen_helper_mve_vcvt_rm_ss, gen_helper_mve_vcvt_rm_us },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
MVEGenVCVTRmodeFn *fn = fns[a->size][u];
|
||||
|
||||
if (!dc_isar_feature(aa32_mve_fp, s) ||
|
||||
!mve_check_qreg_bank(s, a->qd | a->qm) ||
|
||||
!fn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mve_eci_check(s) || !vfp_access_check(s)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
qd = mve_qreg_ptr(a->qd);
|
||||
qm = mve_qreg_ptr(a->qm);
|
||||
fn(cpu_env, qd, qm, tcg_constant_i32(arm_rmode_to_sf(rmode)));
|
||||
tcg_temp_free_ptr(qd);
|
||||
tcg_temp_free_ptr(qm);
|
||||
mve_update_eci(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
#define DO_VCVT_RMODE(INSN, RMODE, U) \
|
||||
static bool trans_##INSN(DisasContext *s, arg_1op *a) \
|
||||
{ \
|
||||
return do_vcvt_rmode(s, a, RMODE, U); \
|
||||
} \
|
||||
|
||||
DO_VCVT_RMODE(VCVTAS, FPROUNDING_TIEAWAY, false)
|
||||
DO_VCVT_RMODE(VCVTAU, FPROUNDING_TIEAWAY, true)
|
||||
DO_VCVT_RMODE(VCVTNS, FPROUNDING_TIEEVEN, false)
|
||||
DO_VCVT_RMODE(VCVTNU, FPROUNDING_TIEEVEN, true)
|
||||
DO_VCVT_RMODE(VCVTPS, FPROUNDING_POSINF, false)
|
||||
DO_VCVT_RMODE(VCVTPU, FPROUNDING_POSINF, true)
|
||||
DO_VCVT_RMODE(VCVTMS, FPROUNDING_NEGINF, false)
|
||||
DO_VCVT_RMODE(VCVTMU, FPROUNDING_NEGINF, true)
|
||||
|
||||
#define DO_VCVT_SH(INSN, FN) \
|
||||
static bool trans_##INSN(DisasContext *s, arg_1op *a) \
|
||||
{ \
|
||||
if (!dc_isar_feature(aa32_mve_fp, s)) { \
|
||||
return false; \
|
||||
} \
|
||||
return do_1op(s, a, gen_helper_mve_##FN); \
|
||||
} \
|
||||
|
||||
DO_VCVT_SH(VCVTB_SH, vcvtb_sh)
|
||||
DO_VCVT_SH(VCVTT_SH, vcvtt_sh)
|
||||
DO_VCVT_SH(VCVTB_HS, vcvtb_hs)
|
||||
DO_VCVT_SH(VCVTT_HS, vcvtt_hs)
|
||||
|
||||
#define DO_VRINT(INSN, RMODE) \
|
||||
static void gen_##INSN##h(TCGv_ptr env, TCGv_ptr qd, TCGv_ptr qm) \
|
||||
{ \
|
||||
gen_helper_mve_vrint_rm_h(env, qd, qm, \
|
||||
tcg_constant_i32(arm_rmode_to_sf(RMODE))); \
|
||||
} \
|
||||
static void gen_##INSN##s(TCGv_ptr env, TCGv_ptr qd, TCGv_ptr qm) \
|
||||
{ \
|
||||
gen_helper_mve_vrint_rm_s(env, qd, qm, \
|
||||
tcg_constant_i32(arm_rmode_to_sf(RMODE))); \
|
||||
} \
|
||||
static bool trans_##INSN(DisasContext *s, arg_1op *a) \
|
||||
{ \
|
||||
static MVEGenOneOpFn * const fns[] = { \
|
||||
NULL, \
|
||||
gen_##INSN##h, \
|
||||
gen_##INSN##s, \
|
||||
NULL, \
|
||||
}; \
|
||||
if (!dc_isar_feature(aa32_mve_fp, s)) { \
|
||||
return false; \
|
||||
} \
|
||||
return do_1op(s, a, fns[a->size]); \
|
||||
}
|
||||
|
||||
DO_VRINT(VRINTN, FPROUNDING_TIEEVEN)
|
||||
DO_VRINT(VRINTA, FPROUNDING_TIEAWAY)
|
||||
DO_VRINT(VRINTZ, FPROUNDING_ZERO)
|
||||
DO_VRINT(VRINTM, FPROUNDING_NEGINF)
|
||||
DO_VRINT(VRINTP, FPROUNDING_POSINF)
|
||||
|
||||
static bool trans_VRINTX(DisasContext *s, arg_1op *a)
|
||||
{
|
||||
static MVEGenOneOpFn * const fns[] = {
|
||||
NULL,
|
||||
gen_helper_mve_vrintx_h,
|
||||
gen_helper_mve_vrintx_s,
|
||||
NULL,
|
||||
};
|
||||
if (!dc_isar_feature(aa32_mve_fp, s)) {
|
||||
return false;
|
||||
}
|
||||
return do_1op(s, a, fns[a->size]);
|
||||
}
|
||||
|
||||
/* Narrowing moves: only size 0 and 1 are valid */
|
||||
#define DO_VMOVN(INSN, FN) \
|
||||
static bool trans_##INSN(DisasContext *s, arg_1op *a) \
|
||||
|
@ -831,6 +974,42 @@ static bool trans_VSBCI(DisasContext *s, arg_2op *a)
|
|||
return do_2op(s, a, gen_helper_mve_vsbci);
|
||||
}
|
||||
|
||||
#define DO_2OP_FP(INSN, FN) \
|
||||
static bool trans_##INSN(DisasContext *s, arg_2op *a) \
|
||||
{ \
|
||||
static MVEGenTwoOpFn * const fns[] = { \
|
||||
NULL, \
|
||||
gen_helper_mve_##FN##h, \
|
||||
gen_helper_mve_##FN##s, \
|
||||
NULL, \
|
||||
}; \
|
||||
if (!dc_isar_feature(aa32_mve_fp, s)) { \
|
||||
return false; \
|
||||
} \
|
||||
return do_2op(s, a, fns[a->size]); \
|
||||
}
|
||||
|
||||
DO_2OP_FP(VADD_fp, vfadd)
|
||||
DO_2OP_FP(VSUB_fp, vfsub)
|
||||
DO_2OP_FP(VMUL_fp, vfmul)
|
||||
DO_2OP_FP(VABD_fp, vfabd)
|
||||
DO_2OP_FP(VMAXNM, vmaxnm)
|
||||
DO_2OP_FP(VMINNM, vminnm)
|
||||
DO_2OP_FP(VCADD90_fp, vfcadd90)
|
||||
DO_2OP_FP(VCADD270_fp, vfcadd270)
|
||||
DO_2OP_FP(VFMA, vfma)
|
||||
DO_2OP_FP(VFMS, vfms)
|
||||
DO_2OP_FP(VCMUL0, vcmul0)
|
||||
DO_2OP_FP(VCMUL90, vcmul90)
|
||||
DO_2OP_FP(VCMUL180, vcmul180)
|
||||
DO_2OP_FP(VCMUL270, vcmul270)
|
||||
DO_2OP_FP(VCMLA0, vcmla0)
|
||||
DO_2OP_FP(VCMLA90, vcmla90)
|
||||
DO_2OP_FP(VCMLA180, vcmla180)
|
||||
DO_2OP_FP(VCMLA270, vcmla270)
|
||||
DO_2OP_FP(VMAXNMA, vmaxnma)
|
||||
DO_2OP_FP(VMINNMA, vminnma)
|
||||
|
||||
static bool do_2op_scalar(DisasContext *s, arg_2scalar *a,
|
||||
MVEGenTwoOpScalarFn fn)
|
||||
{
|
||||
|
@ -861,7 +1040,7 @@ static bool do_2op_scalar(DisasContext *s, arg_2scalar *a,
|
|||
return true;
|
||||
}
|
||||
|
||||
#define DO_2OP_SCALAR(INSN, FN) \
|
||||
#define DO_2OP_SCALAR(INSN, FN) \
|
||||
static bool trans_##INSN(DisasContext *s, arg_2scalar *a) \
|
||||
{ \
|
||||
static MVEGenTwoOpScalarFn * const fns[] = { \
|
||||
|
@ -924,6 +1103,28 @@ static bool trans_VQDMULLT_scalar(DisasContext *s, arg_2scalar *a)
|
|||
return do_2op_scalar(s, a, fns[a->size]);
|
||||
}
|
||||
|
||||
|
||||
#define DO_2OP_FP_SCALAR(INSN, FN) \
|
||||
static bool trans_##INSN(DisasContext *s, arg_2scalar *a) \
|
||||
{ \
|
||||
static MVEGenTwoOpScalarFn * const fns[] = { \
|
||||
NULL, \
|
||||
gen_helper_mve_##FN##h, \
|
||||
gen_helper_mve_##FN##s, \
|
||||
NULL, \
|
||||
}; \
|
||||
if (!dc_isar_feature(aa32_mve_fp, s)) { \
|
||||
return false; \
|
||||
} \
|
||||
return do_2op_scalar(s, a, fns[a->size]); \
|
||||
}
|
||||
|
||||
DO_2OP_FP_SCALAR(VADD_fp_scalar, vfadd_scalar)
|
||||
DO_2OP_FP_SCALAR(VSUB_fp_scalar, vfsub_scalar)
|
||||
DO_2OP_FP_SCALAR(VMUL_fp_scalar, vfmul_scalar)
|
||||
DO_2OP_FP_SCALAR(VFMA_scalar, vfma_scalar)
|
||||
DO_2OP_FP_SCALAR(VFMAS_scalar, vfmas_scalar)
|
||||
|
||||
static bool do_long_dual_acc(DisasContext *s, arg_vmlaldav *a,
|
||||
MVEGenLongDualAccOpFn *fn)
|
||||
{
|
||||
|
@ -1381,6 +1582,24 @@ DO_2SHIFT(VRSHRI_U, vrshli_u, true)
|
|||
DO_2SHIFT(VSRI, vsri, false)
|
||||
DO_2SHIFT(VSLI, vsli, false)
|
||||
|
||||
#define DO_2SHIFT_FP(INSN, FN) \
|
||||
static bool trans_##INSN(DisasContext *s, arg_2shift *a) \
|
||||
{ \
|
||||
if (!dc_isar_feature(aa32_mve_fp, s)) { \
|
||||
return false; \
|
||||
} \
|
||||
return do_2shift(s, a, gen_helper_mve_##FN, false); \
|
||||
}
|
||||
|
||||
DO_2SHIFT_FP(VCVT_SH_fixed, vcvt_sh)
|
||||
DO_2SHIFT_FP(VCVT_UH_fixed, vcvt_uh)
|
||||
DO_2SHIFT_FP(VCVT_HS_fixed, vcvt_hs)
|
||||
DO_2SHIFT_FP(VCVT_HU_fixed, vcvt_hu)
|
||||
DO_2SHIFT_FP(VCVT_SF_fixed, vcvt_sf)
|
||||
DO_2SHIFT_FP(VCVT_UF_fixed, vcvt_uf)
|
||||
DO_2SHIFT_FP(VCVT_FS_fixed, vcvt_fs)
|
||||
DO_2SHIFT_FP(VCVT_FU_fixed, vcvt_fu)
|
||||
|
||||
static bool do_2shift_scalar(DisasContext *s, arg_shl_scalar *a,
|
||||
MVEGenTwoOpShiftFn *fn)
|
||||
{
|
||||
|
@ -1700,6 +1919,42 @@ DO_VCMP(VCMPLT, vcmplt)
|
|||
DO_VCMP(VCMPGT, vcmpgt)
|
||||
DO_VCMP(VCMPLE, vcmple)
|
||||
|
||||
#define DO_VCMP_FP(INSN, FN) \
|
||||
static bool trans_##INSN(DisasContext *s, arg_vcmp *a) \
|
||||
{ \
|
||||
static MVEGenCmpFn * const fns[] = { \
|
||||
NULL, \
|
||||
gen_helper_mve_##FN##h, \
|
||||
gen_helper_mve_##FN##s, \
|
||||
NULL, \
|
||||
}; \
|
||||
if (!dc_isar_feature(aa32_mve_fp, s)) { \
|
||||
return false; \
|
||||
} \
|
||||
return do_vcmp(s, a, fns[a->size]); \
|
||||
} \
|
||||
static bool trans_##INSN##_scalar(DisasContext *s, \
|
||||
arg_vcmp_scalar *a) \
|
||||
{ \
|
||||
static MVEGenScalarCmpFn * const fns[] = { \
|
||||
NULL, \
|
||||
gen_helper_mve_##FN##_scalarh, \
|
||||
gen_helper_mve_##FN##_scalars, \
|
||||
NULL, \
|
||||
}; \
|
||||
if (!dc_isar_feature(aa32_mve_fp, s)) { \
|
||||
return false; \
|
||||
} \
|
||||
return do_vcmp_scalar(s, a, fns[a->size]); \
|
||||
}
|
||||
|
||||
DO_VCMP_FP(VCMPEQ_fp, vfcmpeq)
|
||||
DO_VCMP_FP(VCMPNE_fp, vfcmpne)
|
||||
DO_VCMP_FP(VCMPGE_fp, vfcmpge)
|
||||
DO_VCMP_FP(VCMPLT_fp, vfcmplt)
|
||||
DO_VCMP_FP(VCMPGT_fp, vfcmpgt)
|
||||
DO_VCMP_FP(VCMPLE_fp, vfcmple)
|
||||
|
||||
static bool do_vmaxv(DisasContext *s, arg_vmaxv *a, MVEGenVADDVFn fn)
|
||||
{
|
||||
/*
|
||||
|
@ -1748,6 +2003,26 @@ DO_VMAXV(VMINV_S, vminvs)
|
|||
DO_VMAXV(VMINV_U, vminvu)
|
||||
DO_VMAXV(VMINAV, vminav)
|
||||
|
||||
#define DO_VMAXV_FP(INSN, FN) \
|
||||
static bool trans_##INSN(DisasContext *s, arg_vmaxv *a) \
|
||||
{ \
|
||||
static MVEGenVADDVFn * const fns[] = { \
|
||||
NULL, \
|
||||
gen_helper_mve_##FN##h, \
|
||||
gen_helper_mve_##FN##s, \
|
||||
NULL, \
|
||||
}; \
|
||||
if (!dc_isar_feature(aa32_mve_fp, s)) { \
|
||||
return false; \
|
||||
} \
|
||||
return do_vmaxv(s, a, fns[a->size]); \
|
||||
}
|
||||
|
||||
DO_VMAXV_FP(VMAXNMV, vmaxnmv)
|
||||
DO_VMAXV_FP(VMINNMV, vminnmv)
|
||||
DO_VMAXV_FP(VMAXNMAV, vmaxnmav)
|
||||
DO_VMAXV_FP(VMINNMAV, vminnmav)
|
||||
|
||||
static bool do_vabav(DisasContext *s, arg_vabav *a, MVEGenVABAVFn *fn)
|
||||
{
|
||||
/* Absolute difference accumulated across vector */
|
||||
|
|
|
@ -28,12 +28,6 @@
|
|||
#include "translate.h"
|
||||
#include "translate-a32.h"
|
||||
|
||||
static inline int neon_3same_fp_size(DisasContext *s, int x)
|
||||
{
|
||||
/* Convert 0==fp32, 1==fp16 into a MO_* value */
|
||||
return MO_32 - x;
|
||||
}
|
||||
|
||||
/* Include the generated Neon decoder */
|
||||
#include "decode-neon-dp.c.inc"
|
||||
#include "decode-neon-ls.c.inc"
|
||||
|
|
|
@ -181,6 +181,12 @@ static inline int rsub_8(DisasContext *s, int x)
|
|||
return 8 - x;
|
||||
}
|
||||
|
||||
static inline int neon_3same_fp_size(DisasContext *s, int x)
|
||||
{
|
||||
/* Convert 0==fp32, 1==fp16 into a MO_* value */
|
||||
return MO_32 - x;
|
||||
}
|
||||
|
||||
static inline int arm_dc_feature(DisasContext *dc, int feature)
|
||||
{
|
||||
return (dc->features & (1ULL << feature)) != 0;
|
||||
|
|
|
@ -475,7 +475,7 @@ class BootLinuxConsole(LinuxKernelTest):
|
|||
def test_arm_raspi2_uart0(self):
|
||||
"""
|
||||
:avocado: tags=arch:arm
|
||||
:avocado: tags=machine:raspi2
|
||||
:avocado: tags=machine:raspi2b
|
||||
:avocado: tags=device:pl011
|
||||
:avocado: tags=accel:tcg
|
||||
"""
|
||||
|
@ -484,7 +484,7 @@ class BootLinuxConsole(LinuxKernelTest):
|
|||
def test_arm_raspi2_initrd(self):
|
||||
"""
|
||||
:avocado: tags=arch:arm
|
||||
:avocado: tags=machine:raspi2
|
||||
:avocado: tags=machine:raspi2b
|
||||
"""
|
||||
deb_url = ('http://archive.raspberrypi.org/debian/'
|
||||
'pool/main/r/raspberrypi-firmware/'
|
||||
|
@ -971,7 +971,7 @@ class BootLinuxConsole(LinuxKernelTest):
|
|||
def test_aarch64_raspi3_atf(self):
|
||||
"""
|
||||
:avocado: tags=arch:aarch64
|
||||
:avocado: tags=machine:raspi3
|
||||
:avocado: tags=machine:raspi3b
|
||||
:avocado: tags=cpu:cortex-a53
|
||||
:avocado: tags=device:pl011
|
||||
:avocado: tags=atf
|
||||
|
|
|
@ -473,6 +473,19 @@ static void test_query_cpu_model_expansion(const void *data)
|
|||
assert_has_feature_enabled(qts, "cortex-a57", "pmu");
|
||||
assert_has_feature_enabled(qts, "cortex-a57", "aarch64");
|
||||
|
||||
assert_has_feature_enabled(qts, "a64fx", "pmu");
|
||||
assert_has_feature_enabled(qts, "a64fx", "aarch64");
|
||||
/*
|
||||
* A64FX does not support any other vector lengths besides those
|
||||
* that are enabled by default(128bit, 256bits, 512bit).
|
||||
*/
|
||||
assert_has_feature_enabled(qts, "a64fx", "sve");
|
||||
assert_sve_vls(qts, "a64fx", 0xb, NULL);
|
||||
assert_error(qts, "a64fx", "cannot enable sve384",
|
||||
"{ 'sve384': true }");
|
||||
assert_error(qts, "a64fx", "cannot enable sve640",
|
||||
"{ 'sve640': true }");
|
||||
|
||||
sve_tests_default(qts, "max");
|
||||
pauth_tests_default(qts, "max");
|
||||
|
||||
|
|
|
@ -173,7 +173,7 @@ static testdef_t tests[] = {
|
|||
sizeof(kernel_pls3adsp1800), kernel_pls3adsp1800 },
|
||||
{ "microblazeel", "petalogix-ml605", "", "TT",
|
||||
sizeof(kernel_plml605), kernel_plml605 },
|
||||
{ "arm", "raspi2", "", "TT", sizeof(bios_raspi2), 0, bios_raspi2 },
|
||||
{ "arm", "raspi2b", "", "TT", sizeof(bios_raspi2), 0, bios_raspi2 },
|
||||
/* For hppa, force bios to output to serial by disabling graphics. */
|
||||
{ "hppa", "hppa", "-vga none", "SeaBIOS wants SYSTEM HALT" },
|
||||
{ "aarch64", "virt", "-cpu max", "TT", sizeof(kernel_aarch64),
|
||||
|
|
|
@ -42,7 +42,7 @@ static void *raspi2_get_driver(void *object, const char *interface)
|
|||
return &machine->alloc;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in arm/raspi2\n", interface);
|
||||
fprintf(stderr, "%s not present in arm/raspi2b\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ static QOSGraphObject *raspi2_get_device(void *obj, const char *device)
|
|||
return &machine->sdhci.obj;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in arm/raspi2\n", device);
|
||||
fprintf(stderr, "%s not present in arm/raspi2b\n", device);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
|
@ -85,8 +85,8 @@ static void *qos_create_machine_arm_raspi2(QTestState *qts)
|
|||
|
||||
static void raspi2_register_nodes(void)
|
||||
{
|
||||
qos_node_create_machine("arm/raspi2", qos_create_machine_arm_raspi2);
|
||||
qos_node_contains("arm/raspi2", "generic-sdhci", NULL);
|
||||
qos_node_create_machine("arm/raspi2b", qos_create_machine_arm_raspi2);
|
||||
qos_node_contains("arm/raspi2b", "generic-sdhci", NULL);
|
||||
}
|
||||
|
||||
libqos_init(raspi2_register_nodes);
|
||||
|
|
|
@ -252,17 +252,17 @@ void qos_node_create_driver_named(const char *name, const char *qemu_name,
|
|||
* This function can be useful when there are multiple devices
|
||||
* with the same node name contained in a machine/other node
|
||||
*
|
||||
* For example, if ``arm/raspi2`` contains 2 ``generic-sdhci``
|
||||
* For example, if ``arm/raspi2b`` contains 2 ``generic-sdhci``
|
||||
* devices, the right commands will be:
|
||||
*
|
||||
* .. code::
|
||||
*
|
||||
* qos_node_create_machine("arm/raspi2");
|
||||
* qos_node_create_machine("arm/raspi2b");
|
||||
* qos_node_create_driver("generic-sdhci", constructor);
|
||||
* // assume rest of the fields are set NULL
|
||||
* QOSGraphEdgeOptions op1 = { .edge_name = "emmc" };
|
||||
* QOSGraphEdgeOptions op2 = { .edge_name = "sdcard" };
|
||||
* qos_node_contains("arm/raspi2", "generic-sdhci", &op1, &op2, NULL);
|
||||
* qos_node_contains("arm/raspi2b", "generic-sdhci", &op1, &op2, NULL);
|
||||
*
|
||||
* Of course this also requires that the @container's get_device function
|
||||
* should implement a case for "emmc" and "sdcard".
|
||||
|
|
|
@ -230,7 +230,7 @@ void qos_graph_foreach_test_path(QOSTestCallback fn);
|
|||
/**
|
||||
* qos_get_machine_type(): return QEMU machine type for a machine node.
|
||||
* This function requires every machine @name to be in the form
|
||||
* <arch>/<machine_name>, like "arm/raspi2" or "x86_64/pc".
|
||||
* <arch>/<machine_name>, like "arm/raspi2b" or "x86_64/pc".
|
||||
*
|
||||
* The function will validate the format and return a pointer to
|
||||
* @machine to <machine_name>. For example, when passed "x86_64/pc"
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include "../qtest/libqos/qgraph_internal.h"
|
||||
|
||||
#define MACHINE_PC "x86_64/pc"
|
||||
#define MACHINE_RASPI2 "arm/raspi2"
|
||||
#define MACHINE_RASPI2 "arm/raspi2b"
|
||||
#define I440FX "i440FX-pcihost"
|
||||
#define PCIBUS_PC "pcibus-pc"
|
||||
#define SDHCI "sdhci"
|
||||
|
|
Loading…
Reference in New Issue