mirror of https://gitee.com/openkylin/qemu.git
target-arm queue:
* more A64 Neon instructions * AArch32 VCVTB and VCVTT ARMv8 instructions * fixes to inaccuracies in GIC emulation * libvixl disassembler for A64 * Allwinner SoC ethernet controller * zynq software system reset support -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABCAAGBQJS9lLhAAoJEDwlJe0UNgzewoEP/1ilCOwrepJ4zkIfhzoFpyu6 OtS/H6ORzE/pAr3r890CPEMPCgQZ4Fw5BdUloo/BtIC/fETEPaxAxIdDFvvVwSo6 atX/RSHdSHbJwcwzJ2YW3sHFeZrHX2V0Qzx/bKPu20qcFE39eG4+b3tJfoSURClE o3Jis32j5MLTGphp9ulhfOVVt14ToTqk6M2mDpBVkK2XofKD3ytYMG6els2n04D4 6JTdXDRN53+MfF9B5VoQGlc90OsIQHYycnEMjVGSr1Clxd3AHNYaMNZ+kSLbILZi uYE3s6xwQHBh5L3NuQU6zIrx7MBtA2flNA2BiQiabJaN76qkvZHlkxTIm9D0UlRC xVbSEp3pwu/KZGCtZobcK/dkrJOsGxOJeswDxEQy/MJUneSMnpDRuOwKO/zG7pW5 nXV+fUAVH+xR9PZUvLzpZAxSFLH4A5kR7ezQkl8SLX6+zTN6bH1e7If/Z0c0WffH XNvEarVCbKkbBh/8FOvhJoYuz902ZFnaje1pi26dmxpd7fyswlHkR3ewLYJLlOXq 26lG/aVqMJ+JqzvzL0PpWX9+Am43sVkRhNiedT7RWyu20eCGWjwVvl0TISw7gFge 0yRUhdMQtcdVhREqUScxDyGQrtN0+5U77yIRs+SVH3hjDu6riTbeHQ4uJakJssVn 03Fs0aFWNIv9g7cGhg8C =6kX6 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20140208' into staging target-arm queue: * more A64 Neon instructions * AArch32 VCVTB and VCVTT ARMv8 instructions * fixes to inaccuracies in GIC emulation * libvixl disassembler for A64 * Allwinner SoC ethernet controller * zynq software system reset support # gpg: Signature made Sat 08 Feb 2014 15:53:05 GMT using RSA key ID 14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" * remotes/pmaydell/tags/pull-target-arm-20140208: (29 commits) arm/zynq: Add software system reset via SCLR hw/arm/allwinner-a10: initialize EMAC hw/net: add support for Allwinner EMAC Fast Ethernet controller util/fifo8: clear fifo head upon reset util/fifo8: implement push/pop of multiple bytes disas: Implement disassembly output for A64 disas/libvixl: Fix upstream libvixl compilation issues disas: Add subset of libvixl sources for A64 disassembler rules.mak: Link with C++ if we have a C++ compiler rules.mak: Support .cc as a C++ source file suffix arm_gic: Add GICC_APRn state to the GICState vmstate: Add uint32 2D-array support arm_gic: Support setting/getting binary point reg arm_gic: Keep track of SGI sources arm_gic: Fix GIC pending behavior target-arm: Add support for AArch32 64bit VCVTB and VCVTT target-arm: A64: Add FNEG and FABS to the SIMD 2-reg-misc group target-arm: A64: Add 2-reg-misc REV* instructions target-arm: A64: Add narrowing 2-reg-misc instructions target-arm: A64: Implement 2-reg-misc CNT, NOT and RBIT ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
8fa7574904
|
@ -4641,6 +4641,10 @@ for i in $ARCH $TARGET_BASE_ARCH ; do
|
|||
arm)
|
||||
echo "CONFIG_ARM_DIS=y" >> $config_target_mak
|
||||
echo "CONFIG_ARM_DIS=y" >> config-all-disas.mak
|
||||
if test -n "${cxx}"; then
|
||||
echo "CONFIG_ARM_A64_DIS=y" >> $config_target_mak
|
||||
echo "CONFIG_ARM_A64_DIS=y" >> config-all-disas.mak
|
||||
fi
|
||||
;;
|
||||
cris)
|
||||
echo "CONFIG_CRIS_DIS=y" >> $config_target_mak
|
||||
|
|
|
@ -27,6 +27,7 @@ CONFIG_SSI_SD=y
|
|||
CONFIG_SSI_M25P80=y
|
||||
CONFIG_LAN9118=y
|
||||
CONFIG_SMC91C111=y
|
||||
CONFIG_ALLWINNER_EMAC=y
|
||||
CONFIG_DS1338=y
|
||||
CONFIG_PFLASH_CFI01=y
|
||||
CONFIG_PFLASH_CFI02=y
|
||||
|
|
14
disas.c
14
disas.c
|
@ -190,7 +190,7 @@ static int print_insn_od_target(bfd_vma pc, disassemble_info *info)
|
|||
/* Disassemble this for me please... (debugging). 'flags' has the following
|
||||
values:
|
||||
i386 - 1 means 16 bit code, 2 means 64 bit code
|
||||
arm - bit 0 = thumb, bit 1 = reverse endian
|
||||
arm - bit 0 = thumb, bit 1 = reverse endian, bit 2 = A64
|
||||
ppc - nonzero means little endian
|
||||
other targets - unused
|
||||
*/
|
||||
|
@ -225,7 +225,15 @@ void target_disas(FILE *out, CPUArchState *env, target_ulong code,
|
|||
}
|
||||
print_insn = print_insn_i386;
|
||||
#elif defined(TARGET_ARM)
|
||||
if (flags & 1) {
|
||||
if (flags & 4) {
|
||||
/* We might not be compiled with the A64 disassembler
|
||||
* because it needs a C++ compiler; in that case we will
|
||||
* fall through to the default print_insn_od case.
|
||||
*/
|
||||
#if defined(CONFIG_ARM_A64_DIS)
|
||||
print_insn = print_insn_arm_a64;
|
||||
#endif
|
||||
} else if (flags & 1) {
|
||||
print_insn = print_insn_thumb1;
|
||||
} else {
|
||||
print_insn = print_insn_arm;
|
||||
|
@ -356,6 +364,8 @@ void disas(FILE *out, void *code, unsigned long size)
|
|||
#elif defined(_ARCH_PPC)
|
||||
s.info.disassembler_options = (char *)"any";
|
||||
print_insn = print_insn_ppc;
|
||||
#elif defined(__aarch64__) && defined(CONFIG_ARM_A64_DIS)
|
||||
print_insn = print_insn_arm_a64;
|
||||
#elif defined(__alpha__)
|
||||
print_insn = print_insn_alpha;
|
||||
#elif defined(__sparc__)
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
|
||||
common-obj-$(CONFIG_ALPHA_DIS) += alpha.o
|
||||
common-obj-$(CONFIG_ARM_DIS) += arm.o
|
||||
common-obj-$(CONFIG_ARM_A64_DIS) += arm-a64.o
|
||||
common-obj-$(CONFIG_ARM_A64_DIS) += libvixl/
|
||||
libvixldir = $(SRC_PATH)/disas/libvixl
|
||||
$(obj)/arm-a64.o: QEMU_CFLAGS += -I$(libvixldir)
|
||||
common-obj-$(CONFIG_CRIS_DIS) += cris.o
|
||||
common-obj-$(CONFIG_HPPA_DIS) += hppa.o
|
||||
common-obj-$(CONFIG_I386_DIS) += i386.o
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* ARM A64 disassembly output wrapper to libvixl
|
||||
* Copyright (c) 2013 Linaro Limited
|
||||
* Written by Claudio Fontana
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "a64/disasm-a64.h"
|
||||
|
||||
extern "C" {
|
||||
#include "disas/bfd.h"
|
||||
}
|
||||
|
||||
using namespace vixl;
|
||||
|
||||
static Decoder *vixl_decoder = NULL;
|
||||
static Disassembler *vixl_disasm = NULL;
|
||||
|
||||
/* We don't use libvixl's PrintDisassembler because its output
|
||||
* is a little unhelpful (trailing newlines, for example).
|
||||
* Instead we use our own very similar variant so we have
|
||||
* control over the format.
|
||||
*/
|
||||
class QEMUDisassembler : public Disassembler {
|
||||
public:
|
||||
explicit QEMUDisassembler(FILE *stream) : stream_(stream) { }
|
||||
~QEMUDisassembler() { }
|
||||
|
||||
protected:
|
||||
void ProcessOutput(Instruction *instr) {
|
||||
fprintf(stream_, "%08" PRIx32 " %s",
|
||||
instr->InstructionBits(), GetOutput());
|
||||
}
|
||||
|
||||
private:
|
||||
FILE *stream_;
|
||||
};
|
||||
|
||||
static int vixl_is_initialized(void)
|
||||
{
|
||||
return vixl_decoder != NULL;
|
||||
}
|
||||
|
||||
static void vixl_init(FILE *f) {
|
||||
vixl_decoder = new Decoder();
|
||||
vixl_disasm = new QEMUDisassembler(f);
|
||||
vixl_decoder->AppendVisitor(vixl_disasm);
|
||||
}
|
||||
|
||||
#define INSN_SIZE 4
|
||||
|
||||
/* Disassemble ARM A64 instruction. This is our only entry
|
||||
* point from QEMU's C code.
|
||||
*/
|
||||
int print_insn_arm_a64(uint64_t addr, disassemble_info *info)
|
||||
{
|
||||
uint8_t bytes[INSN_SIZE];
|
||||
uint32_t instr;
|
||||
int status;
|
||||
|
||||
status = info->read_memory_func(addr, bytes, INSN_SIZE, info);
|
||||
if (status != 0) {
|
||||
info->memory_error_func(status, addr, info);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!vixl_is_initialized()) {
|
||||
vixl_init(info->stream);
|
||||
}
|
||||
|
||||
instr = bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24;
|
||||
vixl_decoder->Decode(reinterpret_cast<Instruction*>(&instr));
|
||||
|
||||
return INSN_SIZE;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
LICENCE
|
||||
=======
|
||||
|
||||
The software in this repository is covered by the following licence.
|
||||
|
||||
// Copyright 2013, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,8 @@
|
|||
libvixl_OBJS = utils.o \
|
||||
a64/instructions-a64.o \
|
||||
a64/decoder-a64.o \
|
||||
a64/disasm-a64.o
|
||||
|
||||
$(addprefix $(obj)/,$(libvixl_OBJS)): QEMU_CFLAGS += -I$(SRC_PATH)/disas/libvixl
|
||||
|
||||
common-obj-$(CONFIG_ARM_A64_DIS) += $(libvixl_OBJS)
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
The code in this directory is a subset of libvixl:
|
||||
https://github.com/armvixl/vixl
|
||||
(specifically, it is the set of files needed for disassembly only,
|
||||
taken from libvixl 1.1).
|
||||
Bugfixes should preferably be sent upstream initially.
|
||||
|
||||
The disassembler does not currently support the entire A64 instruction
|
||||
set. Notably:
|
||||
* No Advanced SIMD support.
|
||||
* Limited support for system instructions.
|
||||
* A few miscellaneous integer and floating point instructions are missing.
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,56 @@
|
|||
// Copyright 2013, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef VIXL_CPU_A64_H
|
||||
#define VIXL_CPU_A64_H
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
class CPU {
|
||||
public:
|
||||
// Initialise CPU support.
|
||||
static void SetUp();
|
||||
|
||||
// Ensures the data at a given address and with a given size is the same for
|
||||
// the I and D caches. I and D caches are not automatically coherent on ARM
|
||||
// so this operation is required before any dynamically generated code can
|
||||
// safely run.
|
||||
static void EnsureIAndDCacheCoherency(void *address, size_t length);
|
||||
|
||||
private:
|
||||
// Return the content of the cache type register.
|
||||
static uint32_t GetCacheType();
|
||||
|
||||
// I and D cache line size in bytes.
|
||||
static unsigned icache_line_size_;
|
||||
static unsigned dcache_line_size_;
|
||||
};
|
||||
|
||||
} // namespace vixl
|
||||
|
||||
#endif // VIXL_CPU_A64_H
|
|
@ -0,0 +1,712 @@
|
|||
// Copyright 2013, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "globals.h"
|
||||
#include "utils.h"
|
||||
#include "a64/decoder-a64.h"
|
||||
|
||||
namespace vixl {
|
||||
// Top-level instruction decode function.
|
||||
void Decoder::Decode(Instruction *instr) {
|
||||
if (instr->Bits(28, 27) == 0) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
switch (instr->Bits(27, 24)) {
|
||||
// 0: PC relative addressing.
|
||||
case 0x0: DecodePCRelAddressing(instr); break;
|
||||
|
||||
// 1: Add/sub immediate.
|
||||
case 0x1: DecodeAddSubImmediate(instr); break;
|
||||
|
||||
// A: Logical shifted register.
|
||||
// Add/sub with carry.
|
||||
// Conditional compare register.
|
||||
// Conditional compare immediate.
|
||||
// Conditional select.
|
||||
// Data processing 1 source.
|
||||
// Data processing 2 source.
|
||||
// B: Add/sub shifted register.
|
||||
// Add/sub extended register.
|
||||
// Data processing 3 source.
|
||||
case 0xA:
|
||||
case 0xB: DecodeDataProcessing(instr); break;
|
||||
|
||||
// 2: Logical immediate.
|
||||
// Move wide immediate.
|
||||
case 0x2: DecodeLogical(instr); break;
|
||||
|
||||
// 3: Bitfield.
|
||||
// Extract.
|
||||
case 0x3: DecodeBitfieldExtract(instr); break;
|
||||
|
||||
// 4: Unconditional branch immediate.
|
||||
// Exception generation.
|
||||
// Compare and branch immediate.
|
||||
// 5: Compare and branch immediate.
|
||||
// Conditional branch.
|
||||
// System.
|
||||
// 6,7: Unconditional branch.
|
||||
// Test and branch immediate.
|
||||
case 0x4:
|
||||
case 0x5:
|
||||
case 0x6:
|
||||
case 0x7: DecodeBranchSystemException(instr); break;
|
||||
|
||||
// 8,9: Load/store register pair post-index.
|
||||
// Load register literal.
|
||||
// Load/store register unscaled immediate.
|
||||
// Load/store register immediate post-index.
|
||||
// Load/store register immediate pre-index.
|
||||
// Load/store register offset.
|
||||
// Load/store exclusive.
|
||||
// C,D: Load/store register pair offset.
|
||||
// Load/store register pair pre-index.
|
||||
// Load/store register unsigned immediate.
|
||||
// Advanced SIMD.
|
||||
case 0x8:
|
||||
case 0x9:
|
||||
case 0xC:
|
||||
case 0xD: DecodeLoadStore(instr); break;
|
||||
|
||||
// E: FP fixed point conversion.
|
||||
// FP integer conversion.
|
||||
// FP data processing 1 source.
|
||||
// FP compare.
|
||||
// FP immediate.
|
||||
// FP data processing 2 source.
|
||||
// FP conditional compare.
|
||||
// FP conditional select.
|
||||
// Advanced SIMD.
|
||||
// F: FP data processing 3 source.
|
||||
// Advanced SIMD.
|
||||
case 0xE:
|
||||
case 0xF: DecodeFP(instr); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Decoder::AppendVisitor(DecoderVisitor* new_visitor) {
|
||||
visitors_.remove(new_visitor);
|
||||
visitors_.push_front(new_visitor);
|
||||
}
|
||||
|
||||
|
||||
void Decoder::PrependVisitor(DecoderVisitor* new_visitor) {
|
||||
visitors_.remove(new_visitor);
|
||||
visitors_.push_back(new_visitor);
|
||||
}
|
||||
|
||||
|
||||
void Decoder::InsertVisitorBefore(DecoderVisitor* new_visitor,
|
||||
DecoderVisitor* registered_visitor) {
|
||||
visitors_.remove(new_visitor);
|
||||
std::list<DecoderVisitor*>::iterator it;
|
||||
for (it = visitors_.begin(); it != visitors_.end(); it++) {
|
||||
if (*it == registered_visitor) {
|
||||
visitors_.insert(it, new_visitor);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// We reached the end of the list. The last element must be
|
||||
// registered_visitor.
|
||||
ASSERT(*it == registered_visitor);
|
||||
visitors_.insert(it, new_visitor);
|
||||
}
|
||||
|
||||
|
||||
void Decoder::InsertVisitorAfter(DecoderVisitor* new_visitor,
|
||||
DecoderVisitor* registered_visitor) {
|
||||
visitors_.remove(new_visitor);
|
||||
std::list<DecoderVisitor*>::iterator it;
|
||||
for (it = visitors_.begin(); it != visitors_.end(); it++) {
|
||||
if (*it == registered_visitor) {
|
||||
it++;
|
||||
visitors_.insert(it, new_visitor);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// We reached the end of the list. The last element must be
|
||||
// registered_visitor.
|
||||
ASSERT(*it == registered_visitor);
|
||||
visitors_.push_back(new_visitor);
|
||||
}
|
||||
|
||||
|
||||
void Decoder::RemoveVisitor(DecoderVisitor* visitor) {
|
||||
visitors_.remove(visitor);
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodePCRelAddressing(Instruction* instr) {
|
||||
ASSERT(instr->Bits(27, 24) == 0x0);
|
||||
// We know bit 28 is set, as <b28:b27> = 0 is filtered out at the top level
|
||||
// decode.
|
||||
ASSERT(instr->Bit(28) == 0x1);
|
||||
VisitPCRelAddressing(instr);
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeBranchSystemException(Instruction* instr) {
|
||||
ASSERT((instr->Bits(27, 24) == 0x4) ||
|
||||
(instr->Bits(27, 24) == 0x5) ||
|
||||
(instr->Bits(27, 24) == 0x6) ||
|
||||
(instr->Bits(27, 24) == 0x7) );
|
||||
|
||||
switch (instr->Bits(31, 29)) {
|
||||
case 0:
|
||||
case 4: {
|
||||
VisitUnconditionalBranch(instr);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
case 5: {
|
||||
if (instr->Bit(25) == 0) {
|
||||
VisitCompareBranch(instr);
|
||||
} else {
|
||||
VisitTestBranch(instr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if (instr->Bit(25) == 0) {
|
||||
if ((instr->Bit(24) == 0x1) ||
|
||||
(instr->Mask(0x01000010) == 0x00000010)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitConditionalBranch(instr);
|
||||
}
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
if (instr->Bit(25) == 0) {
|
||||
if (instr->Bit(24) == 0) {
|
||||
if ((instr->Bits(4, 2) != 0) ||
|
||||
(instr->Mask(0x00E0001D) == 0x00200001) ||
|
||||
(instr->Mask(0x00E0001D) == 0x00400001) ||
|
||||
(instr->Mask(0x00E0001E) == 0x00200002) ||
|
||||
(instr->Mask(0x00E0001E) == 0x00400002) ||
|
||||
(instr->Mask(0x00E0001C) == 0x00600000) ||
|
||||
(instr->Mask(0x00E0001C) == 0x00800000) ||
|
||||
(instr->Mask(0x00E0001F) == 0x00A00000) ||
|
||||
(instr->Mask(0x00C0001C) == 0x00C00000)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitException(instr);
|
||||
}
|
||||
} else {
|
||||
if (instr->Bits(23, 22) == 0) {
|
||||
const Instr masked_003FF0E0 = instr->Mask(0x003FF0E0);
|
||||
if ((instr->Bits(21, 19) == 0x4) ||
|
||||
(masked_003FF0E0 == 0x00033000) ||
|
||||
(masked_003FF0E0 == 0x003FF020) ||
|
||||
(masked_003FF0E0 == 0x003FF060) ||
|
||||
(masked_003FF0E0 == 0x003FF0E0) ||
|
||||
(instr->Mask(0x00388000) == 0x00008000) ||
|
||||
(instr->Mask(0x0038E000) == 0x00000000) ||
|
||||
(instr->Mask(0x0039E000) == 0x00002000) ||
|
||||
(instr->Mask(0x003AE000) == 0x00002000) ||
|
||||
(instr->Mask(0x003CE000) == 0x00042000) ||
|
||||
(instr->Mask(0x003FFFC0) == 0x000320C0) ||
|
||||
(instr->Mask(0x003FF100) == 0x00032100) ||
|
||||
(instr->Mask(0x003FF200) == 0x00032200) ||
|
||||
(instr->Mask(0x003FF400) == 0x00032400) ||
|
||||
(instr->Mask(0x003FF800) == 0x00032800) ||
|
||||
(instr->Mask(0x0038F000) == 0x00005000) ||
|
||||
(instr->Mask(0x0038E000) == 0x00006000)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitSystem(instr);
|
||||
}
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((instr->Bit(24) == 0x1) ||
|
||||
(instr->Bits(20, 16) != 0x1F) ||
|
||||
(instr->Bits(15, 10) != 0) ||
|
||||
(instr->Bits(4, 0) != 0) ||
|
||||
(instr->Bits(24, 21) == 0x3) ||
|
||||
(instr->Bits(24, 22) == 0x3)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitUnconditionalBranchToRegister(instr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
case 7: {
|
||||
VisitUnallocated(instr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeLoadStore(Instruction* instr) {
|
||||
ASSERT((instr->Bits(27, 24) == 0x8) ||
|
||||
(instr->Bits(27, 24) == 0x9) ||
|
||||
(instr->Bits(27, 24) == 0xC) ||
|
||||
(instr->Bits(27, 24) == 0xD) );
|
||||
|
||||
if (instr->Bit(24) == 0) {
|
||||
if (instr->Bit(28) == 0) {
|
||||
if (instr->Bit(29) == 0) {
|
||||
if (instr->Bit(26) == 0) {
|
||||
// TODO: VisitLoadStoreExclusive.
|
||||
VisitUnimplemented(instr);
|
||||
} else {
|
||||
DecodeAdvSIMDLoadStore(instr);
|
||||
}
|
||||
} else {
|
||||
if ((instr->Bits(31, 30) == 0x3) ||
|
||||
(instr->Mask(0xC4400000) == 0x40000000)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
if (instr->Bit(23) == 0) {
|
||||
if (instr->Mask(0xC4400000) == 0xC0400000) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitLoadStorePairNonTemporal(instr);
|
||||
}
|
||||
} else {
|
||||
VisitLoadStorePairPostIndex(instr);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (instr->Bit(29) == 0) {
|
||||
if (instr->Mask(0xC4000000) == 0xC4000000) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitLoadLiteral(instr);
|
||||
}
|
||||
} else {
|
||||
if ((instr->Mask(0x84C00000) == 0x80C00000) ||
|
||||
(instr->Mask(0x44800000) == 0x44800000) ||
|
||||
(instr->Mask(0x84800000) == 0x84800000)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
if (instr->Bit(21) == 0) {
|
||||
switch (instr->Bits(11, 10)) {
|
||||
case 0: {
|
||||
VisitLoadStoreUnscaledOffset(instr);
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
if (instr->Mask(0xC4C00000) == 0xC0800000) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitLoadStorePostIndex(instr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// TODO: VisitLoadStoreRegisterOffsetUnpriv.
|
||||
VisitUnimplemented(instr);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
if (instr->Mask(0xC4C00000) == 0xC0800000) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitLoadStorePreIndex(instr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (instr->Bits(11, 10) == 0x2) {
|
||||
if (instr->Bit(14) == 0) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitLoadStoreRegisterOffset(instr);
|
||||
}
|
||||
} else {
|
||||
VisitUnallocated(instr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (instr->Bit(28) == 0) {
|
||||
if (instr->Bit(29) == 0) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
if ((instr->Bits(31, 30) == 0x3) ||
|
||||
(instr->Mask(0xC4400000) == 0x40000000)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
if (instr->Bit(23) == 0) {
|
||||
VisitLoadStorePairOffset(instr);
|
||||
} else {
|
||||
VisitLoadStorePairPreIndex(instr);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (instr->Bit(29) == 0) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
if ((instr->Mask(0x84C00000) == 0x80C00000) ||
|
||||
(instr->Mask(0x44800000) == 0x44800000) ||
|
||||
(instr->Mask(0x84800000) == 0x84800000)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitLoadStoreUnsignedOffset(instr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeLogical(Instruction* instr) {
|
||||
ASSERT(instr->Bits(27, 24) == 0x2);
|
||||
|
||||
if (instr->Mask(0x80400000) == 0x00400000) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
if (instr->Bit(23) == 0) {
|
||||
VisitLogicalImmediate(instr);
|
||||
} else {
|
||||
if (instr->Bits(30, 29) == 0x1) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitMoveWideImmediate(instr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeBitfieldExtract(Instruction* instr) {
|
||||
ASSERT(instr->Bits(27, 24) == 0x3);
|
||||
|
||||
if ((instr->Mask(0x80400000) == 0x80000000) ||
|
||||
(instr->Mask(0x80400000) == 0x00400000) ||
|
||||
(instr->Mask(0x80008000) == 0x00008000)) {
|
||||
VisitUnallocated(instr);
|
||||
} else if (instr->Bit(23) == 0) {
|
||||
if ((instr->Mask(0x80200000) == 0x00200000) ||
|
||||
(instr->Mask(0x60000000) == 0x60000000)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitBitfield(instr);
|
||||
}
|
||||
} else {
|
||||
if ((instr->Mask(0x60200000) == 0x00200000) ||
|
||||
(instr->Mask(0x60000000) != 0x00000000)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitExtract(instr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeAddSubImmediate(Instruction* instr) {
|
||||
ASSERT(instr->Bits(27, 24) == 0x1);
|
||||
if (instr->Bit(23) == 1) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitAddSubImmediate(instr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeDataProcessing(Instruction* instr) {
|
||||
ASSERT((instr->Bits(27, 24) == 0xA) ||
|
||||
(instr->Bits(27, 24) == 0xB) );
|
||||
|
||||
if (instr->Bit(24) == 0) {
|
||||
if (instr->Bit(28) == 0) {
|
||||
if (instr->Mask(0x80008000) == 0x00008000) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitLogicalShifted(instr);
|
||||
}
|
||||
} else {
|
||||
switch (instr->Bits(23, 21)) {
|
||||
case 0: {
|
||||
if (instr->Mask(0x0000FC00) != 0) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitAddSubWithCarry(instr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if ((instr->Bit(29) == 0) ||
|
||||
(instr->Mask(0x00000410) != 0)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
if (instr->Bit(11) == 0) {
|
||||
VisitConditionalCompareRegister(instr);
|
||||
} else {
|
||||
VisitConditionalCompareImmediate(instr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
if (instr->Mask(0x20000800) != 0x00000000) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitConditionalSelect(instr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
if (instr->Bit(29) == 0x1) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
if (instr->Bit(30) == 0) {
|
||||
if ((instr->Bit(15) == 0x1) ||
|
||||
(instr->Bits(15, 11) == 0) ||
|
||||
(instr->Bits(15, 12) == 0x1) ||
|
||||
(instr->Bits(15, 12) == 0x3) ||
|
||||
(instr->Bits(15, 13) == 0x3) ||
|
||||
(instr->Mask(0x8000EC00) == 0x00004C00) ||
|
||||
(instr->Mask(0x8000E800) == 0x80004000) ||
|
||||
(instr->Mask(0x8000E400) == 0x80004000)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitDataProcessing2Source(instr);
|
||||
}
|
||||
} else {
|
||||
if ((instr->Bit(13) == 1) ||
|
||||
(instr->Bits(20, 16) != 0) ||
|
||||
(instr->Bits(15, 14) != 0) ||
|
||||
(instr->Mask(0xA01FFC00) == 0x00000C00) ||
|
||||
(instr->Mask(0x201FF800) == 0x00001800)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitDataProcessing1Source(instr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
case 3:
|
||||
case 5:
|
||||
case 7: VisitUnallocated(instr); break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (instr->Bit(28) == 0) {
|
||||
if (instr->Bit(21) == 0) {
|
||||
if ((instr->Bits(23, 22) == 0x3) ||
|
||||
(instr->Mask(0x80008000) == 0x00008000)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitAddSubShifted(instr);
|
||||
}
|
||||
} else {
|
||||
if ((instr->Mask(0x00C00000) != 0x00000000) ||
|
||||
(instr->Mask(0x00001400) == 0x00001400) ||
|
||||
(instr->Mask(0x00001800) == 0x00001800)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitAddSubExtended(instr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((instr->Bit(30) == 0x1) ||
|
||||
(instr->Bits(30, 29) == 0x1) ||
|
||||
(instr->Mask(0xE0600000) == 0x00200000) ||
|
||||
(instr->Mask(0xE0608000) == 0x00400000) ||
|
||||
(instr->Mask(0x60608000) == 0x00408000) ||
|
||||
(instr->Mask(0x60E00000) == 0x00E00000) ||
|
||||
(instr->Mask(0x60E00000) == 0x00800000) ||
|
||||
(instr->Mask(0x60E00000) == 0x00600000)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitDataProcessing3Source(instr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeFP(Instruction* instr) {
|
||||
ASSERT((instr->Bits(27, 24) == 0xE) ||
|
||||
(instr->Bits(27, 24) == 0xF) );
|
||||
|
||||
if (instr->Bit(28) == 0) {
|
||||
DecodeAdvSIMDDataProcessing(instr);
|
||||
} else {
|
||||
if (instr->Bit(29) == 1) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
if (instr->Bits(31, 30) == 0x3) {
|
||||
VisitUnallocated(instr);
|
||||
} else if (instr->Bits(31, 30) == 0x1) {
|
||||
DecodeAdvSIMDDataProcessing(instr);
|
||||
} else {
|
||||
if (instr->Bit(24) == 0) {
|
||||
if (instr->Bit(21) == 0) {
|
||||
if ((instr->Bit(23) == 1) ||
|
||||
(instr->Bit(18) == 1) ||
|
||||
(instr->Mask(0x80008000) == 0x00000000) ||
|
||||
(instr->Mask(0x000E0000) == 0x00000000) ||
|
||||
(instr->Mask(0x000E0000) == 0x000A0000) ||
|
||||
(instr->Mask(0x00160000) == 0x00000000) ||
|
||||
(instr->Mask(0x00160000) == 0x00120000)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitFPFixedPointConvert(instr);
|
||||
}
|
||||
} else {
|
||||
if (instr->Bits(15, 10) == 32) {
|
||||
VisitUnallocated(instr);
|
||||
} else if (instr->Bits(15, 10) == 0) {
|
||||
if ((instr->Bits(23, 22) == 0x3) ||
|
||||
(instr->Mask(0x000E0000) == 0x000A0000) ||
|
||||
(instr->Mask(0x000E0000) == 0x000C0000) ||
|
||||
(instr->Mask(0x00160000) == 0x00120000) ||
|
||||
(instr->Mask(0x00160000) == 0x00140000) ||
|
||||
(instr->Mask(0x20C40000) == 0x00800000) ||
|
||||
(instr->Mask(0x20C60000) == 0x00840000) ||
|
||||
(instr->Mask(0xA0C60000) == 0x80060000) ||
|
||||
(instr->Mask(0xA0C60000) == 0x00860000) ||
|
||||
(instr->Mask(0xA0C60000) == 0x00460000) ||
|
||||
(instr->Mask(0xA0CE0000) == 0x80860000) ||
|
||||
(instr->Mask(0xA0CE0000) == 0x804E0000) ||
|
||||
(instr->Mask(0xA0CE0000) == 0x000E0000) ||
|
||||
(instr->Mask(0xA0D60000) == 0x00160000) ||
|
||||
(instr->Mask(0xA0D60000) == 0x80560000) ||
|
||||
(instr->Mask(0xA0D60000) == 0x80960000)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitFPIntegerConvert(instr);
|
||||
}
|
||||
} else if (instr->Bits(14, 10) == 16) {
|
||||
const Instr masked_A0DF8000 = instr->Mask(0xA0DF8000);
|
||||
if ((instr->Mask(0x80180000) != 0) ||
|
||||
(masked_A0DF8000 == 0x00020000) ||
|
||||
(masked_A0DF8000 == 0x00030000) ||
|
||||
(masked_A0DF8000 == 0x00068000) ||
|
||||
(masked_A0DF8000 == 0x00428000) ||
|
||||
(masked_A0DF8000 == 0x00430000) ||
|
||||
(masked_A0DF8000 == 0x00468000) ||
|
||||
(instr->Mask(0xA0D80000) == 0x00800000) ||
|
||||
(instr->Mask(0xA0DE0000) == 0x00C00000) ||
|
||||
(instr->Mask(0xA0DF0000) == 0x00C30000) ||
|
||||
(instr->Mask(0xA0DC0000) == 0x00C40000)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitFPDataProcessing1Source(instr);
|
||||
}
|
||||
} else if (instr->Bits(13, 10) == 8) {
|
||||
if ((instr->Bits(15, 14) != 0) ||
|
||||
(instr->Bits(2, 0) != 0) ||
|
||||
(instr->Mask(0x80800000) != 0x00000000)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitFPCompare(instr);
|
||||
}
|
||||
} else if (instr->Bits(12, 10) == 4) {
|
||||
if ((instr->Bits(9, 5) != 0) ||
|
||||
(instr->Mask(0x80800000) != 0x00000000)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitFPImmediate(instr);
|
||||
}
|
||||
} else {
|
||||
if (instr->Mask(0x80800000) != 0x00000000) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
switch (instr->Bits(11, 10)) {
|
||||
case 1: {
|
||||
VisitFPConditionalCompare(instr);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if ((instr->Bits(15, 14) == 0x3) ||
|
||||
(instr->Mask(0x00009000) == 0x00009000) ||
|
||||
(instr->Mask(0x0000A000) == 0x0000A000)) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitFPDataProcessing2Source(instr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
VisitFPConditionalSelect(instr);
|
||||
break;
|
||||
}
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Bit 30 == 1 has been handled earlier.
|
||||
ASSERT(instr->Bit(30) == 0);
|
||||
if (instr->Mask(0xA0800000) != 0) {
|
||||
VisitUnallocated(instr);
|
||||
} else {
|
||||
VisitFPDataProcessing3Source(instr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeAdvSIMDLoadStore(Instruction* instr) {
|
||||
// TODO: Implement Advanced SIMD load/store instruction decode.
|
||||
ASSERT(instr->Bits(29, 25) == 0x6);
|
||||
VisitUnimplemented(instr);
|
||||
}
|
||||
|
||||
|
||||
void Decoder::DecodeAdvSIMDDataProcessing(Instruction* instr) {
|
||||
// TODO: Implement Advanced SIMD data processing instruction decode.
|
||||
ASSERT(instr->Bits(27, 25) == 0x7);
|
||||
VisitUnimplemented(instr);
|
||||
}
|
||||
|
||||
|
||||
#define DEFINE_VISITOR_CALLERS(A) \
|
||||
void Decoder::Visit##A(Instruction *instr) { \
|
||||
ASSERT(instr->Mask(A##FMask) == A##Fixed); \
|
||||
std::list<DecoderVisitor*>::iterator it; \
|
||||
for (it = visitors_.begin(); it != visitors_.end(); it++) { \
|
||||
(*it)->Visit##A(instr); \
|
||||
} \
|
||||
}
|
||||
VISITOR_LIST(DEFINE_VISITOR_CALLERS)
|
||||
#undef DEFINE_VISITOR_CALLERS
|
||||
} // namespace vixl
|
|
@ -0,0 +1,198 @@
|
|||
// Copyright 2013, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef VIXL_A64_DECODER_A64_H_
|
||||
#define VIXL_A64_DECODER_A64_H_
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "globals.h"
|
||||
#include "a64/instructions-a64.h"
|
||||
|
||||
|
||||
// List macro containing all visitors needed by the decoder class.
|
||||
|
||||
#define VISITOR_LIST(V) \
|
||||
V(PCRelAddressing) \
|
||||
V(AddSubImmediate) \
|
||||
V(LogicalImmediate) \
|
||||
V(MoveWideImmediate) \
|
||||
V(Bitfield) \
|
||||
V(Extract) \
|
||||
V(UnconditionalBranch) \
|
||||
V(UnconditionalBranchToRegister) \
|
||||
V(CompareBranch) \
|
||||
V(TestBranch) \
|
||||
V(ConditionalBranch) \
|
||||
V(System) \
|
||||
V(Exception) \
|
||||
V(LoadStorePairPostIndex) \
|
||||
V(LoadStorePairOffset) \
|
||||
V(LoadStorePairPreIndex) \
|
||||
V(LoadStorePairNonTemporal) \
|
||||
V(LoadLiteral) \
|
||||
V(LoadStoreUnscaledOffset) \
|
||||
V(LoadStorePostIndex) \
|
||||
V(LoadStorePreIndex) \
|
||||
V(LoadStoreRegisterOffset) \
|
||||
V(LoadStoreUnsignedOffset) \
|
||||
V(LogicalShifted) \
|
||||
V(AddSubShifted) \
|
||||
V(AddSubExtended) \
|
||||
V(AddSubWithCarry) \
|
||||
V(ConditionalCompareRegister) \
|
||||
V(ConditionalCompareImmediate) \
|
||||
V(ConditionalSelect) \
|
||||
V(DataProcessing1Source) \
|
||||
V(DataProcessing2Source) \
|
||||
V(DataProcessing3Source) \
|
||||
V(FPCompare) \
|
||||
V(FPConditionalCompare) \
|
||||
V(FPConditionalSelect) \
|
||||
V(FPImmediate) \
|
||||
V(FPDataProcessing1Source) \
|
||||
V(FPDataProcessing2Source) \
|
||||
V(FPDataProcessing3Source) \
|
||||
V(FPIntegerConvert) \
|
||||
V(FPFixedPointConvert) \
|
||||
V(Unallocated) \
|
||||
V(Unimplemented)
|
||||
|
||||
namespace vixl {
|
||||
|
||||
// The Visitor interface. Disassembler and simulator (and other tools)
|
||||
// must provide implementations for all of these functions.
|
||||
class DecoderVisitor {
|
||||
public:
|
||||
#define DECLARE(A) virtual void Visit##A(Instruction* instr) = 0;
|
||||
VISITOR_LIST(DECLARE)
|
||||
#undef DECLARE
|
||||
|
||||
virtual ~DecoderVisitor() {}
|
||||
|
||||
private:
|
||||
// Visitors are registered in a list.
|
||||
std::list<DecoderVisitor*> visitors_;
|
||||
|
||||
friend class Decoder;
|
||||
};
|
||||
|
||||
|
||||
class Decoder: public DecoderVisitor {
|
||||
public:
|
||||
Decoder() {}
|
||||
|
||||
// Top-level instruction decoder function. Decodes an instruction and calls
|
||||
// the visitor functions registered with the Decoder class.
|
||||
void Decode(Instruction *instr);
|
||||
|
||||
// Register a new visitor class with the decoder.
|
||||
// Decode() will call the corresponding visitor method from all registered
|
||||
// visitor classes when decoding reaches the leaf node of the instruction
|
||||
// decode tree.
|
||||
// Visitors are called in the order.
|
||||
// A visitor can only be registered once.
|
||||
// Registering an already registered visitor will update its position.
|
||||
//
|
||||
// d.AppendVisitor(V1);
|
||||
// d.AppendVisitor(V2);
|
||||
// d.PrependVisitor(V2); // Move V2 at the start of the list.
|
||||
// d.InsertVisitorBefore(V3, V2);
|
||||
// d.AppendVisitor(V4);
|
||||
// d.AppendVisitor(V4); // No effect.
|
||||
//
|
||||
// d.Decode(i);
|
||||
//
|
||||
// will call in order visitor methods in V3, V2, V1, V4.
|
||||
void AppendVisitor(DecoderVisitor* visitor);
|
||||
void PrependVisitor(DecoderVisitor* visitor);
|
||||
void InsertVisitorBefore(DecoderVisitor* new_visitor,
|
||||
DecoderVisitor* registered_visitor);
|
||||
void InsertVisitorAfter(DecoderVisitor* new_visitor,
|
||||
DecoderVisitor* registered_visitor);
|
||||
|
||||
// Remove a previously registered visitor class from the list of visitors
|
||||
// stored by the decoder.
|
||||
void RemoveVisitor(DecoderVisitor* visitor);
|
||||
|
||||
#define DECLARE(A) void Visit##A(Instruction* instr);
|
||||
VISITOR_LIST(DECLARE)
|
||||
#undef DECLARE
|
||||
|
||||
private:
|
||||
// Decode the PC relative addressing instruction, and call the corresponding
|
||||
// visitors.
|
||||
// On entry, instruction bits 27:24 = 0x0.
|
||||
void DecodePCRelAddressing(Instruction* instr);
|
||||
|
||||
// Decode the add/subtract immediate instruction, and call the correspoding
|
||||
// visitors.
|
||||
// On entry, instruction bits 27:24 = 0x1.
|
||||
void DecodeAddSubImmediate(Instruction* instr);
|
||||
|
||||
// Decode the branch, system command, and exception generation parts of
|
||||
// the instruction tree, and call the corresponding visitors.
|
||||
// On entry, instruction bits 27:24 = {0x4, 0x5, 0x6, 0x7}.
|
||||
void DecodeBranchSystemException(Instruction* instr);
|
||||
|
||||
// Decode the load and store parts of the instruction tree, and call
|
||||
// the corresponding visitors.
|
||||
// On entry, instruction bits 27:24 = {0x8, 0x9, 0xC, 0xD}.
|
||||
void DecodeLoadStore(Instruction* instr);
|
||||
|
||||
// Decode the logical immediate and move wide immediate parts of the
|
||||
// instruction tree, and call the corresponding visitors.
|
||||
// On entry, instruction bits 27:24 = 0x2.
|
||||
void DecodeLogical(Instruction* instr);
|
||||
|
||||
// Decode the bitfield and extraction parts of the instruction tree,
|
||||
// and call the corresponding visitors.
|
||||
// On entry, instruction bits 27:24 = 0x3.
|
||||
void DecodeBitfieldExtract(Instruction* instr);
|
||||
|
||||
// Decode the data processing parts of the instruction tree, and call the
|
||||
// corresponding visitors.
|
||||
// On entry, instruction bits 27:24 = {0x1, 0xA, 0xB}.
|
||||
void DecodeDataProcessing(Instruction* instr);
|
||||
|
||||
// Decode the floating point parts of the instruction tree, and call the
|
||||
// corresponding visitors.
|
||||
// On entry, instruction bits 27:24 = {0xE, 0xF}.
|
||||
void DecodeFP(Instruction* instr);
|
||||
|
||||
// Decode the Advanced SIMD (NEON) load/store part of the instruction tree,
|
||||
// and call the corresponding visitors.
|
||||
// On entry, instruction bits 29:25 = 0x6.
|
||||
void DecodeAdvSIMDLoadStore(Instruction* instr);
|
||||
|
||||
// Decode the Advanced SIMD (NEON) data processing part of the instruction
|
||||
// tree, and call the corresponding visitors.
|
||||
// On entry, instruction bits 27:25 = 0x7.
|
||||
void DecodeAdvSIMDDataProcessing(Instruction* instr);
|
||||
};
|
||||
} // namespace vixl
|
||||
|
||||
#endif // VIXL_A64_DECODER_A64_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,109 @@
|
|||
// Copyright 2013, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef VIXL_A64_DISASM_A64_H
|
||||
#define VIXL_A64_DISASM_A64_H
|
||||
|
||||
#include "globals.h"
|
||||
#include "utils.h"
|
||||
#include "instructions-a64.h"
|
||||
#include "decoder-a64.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
class Disassembler: public DecoderVisitor {
|
||||
public:
|
||||
Disassembler();
|
||||
Disassembler(char* text_buffer, int buffer_size);
|
||||
virtual ~Disassembler();
|
||||
char* GetOutput();
|
||||
|
||||
// Declare all Visitor functions.
|
||||
#define DECLARE(A) void Visit##A(Instruction* instr);
|
||||
VISITOR_LIST(DECLARE)
|
||||
#undef DECLARE
|
||||
|
||||
protected:
|
||||
virtual void ProcessOutput(Instruction* instr);
|
||||
|
||||
private:
|
||||
void Format(Instruction* instr, const char* mnemonic, const char* format);
|
||||
void Substitute(Instruction* instr, const char* string);
|
||||
int SubstituteField(Instruction* instr, const char* format);
|
||||
int SubstituteRegisterField(Instruction* instr, const char* format);
|
||||
int SubstituteImmediateField(Instruction* instr, const char* format);
|
||||
int SubstituteLiteralField(Instruction* instr, const char* format);
|
||||
int SubstituteBitfieldImmediateField(Instruction* instr, const char* format);
|
||||
int SubstituteShiftField(Instruction* instr, const char* format);
|
||||
int SubstituteExtendField(Instruction* instr, const char* format);
|
||||
int SubstituteConditionField(Instruction* instr, const char* format);
|
||||
int SubstitutePCRelAddressField(Instruction* instr, const char* format);
|
||||
int SubstituteBranchTargetField(Instruction* instr, const char* format);
|
||||
int SubstituteLSRegOffsetField(Instruction* instr, const char* format);
|
||||
int SubstitutePrefetchField(Instruction* instr, const char* format);
|
||||
|
||||
inline bool RdIsZROrSP(Instruction* instr) const {
|
||||
return (instr->Rd() == kZeroRegCode);
|
||||
}
|
||||
|
||||
inline bool RnIsZROrSP(Instruction* instr) const {
|
||||
return (instr->Rn() == kZeroRegCode);
|
||||
}
|
||||
|
||||
inline bool RmIsZROrSP(Instruction* instr) const {
|
||||
return (instr->Rm() == kZeroRegCode);
|
||||
}
|
||||
|
||||
inline bool RaIsZROrSP(Instruction* instr) const {
|
||||
return (instr->Ra() == kZeroRegCode);
|
||||
}
|
||||
|
||||
bool IsMovzMovnImm(unsigned reg_size, uint64_t value);
|
||||
|
||||
void ResetOutput();
|
||||
void AppendToOutput(const char* string, ...);
|
||||
|
||||
char* buffer_;
|
||||
uint32_t buffer_pos_;
|
||||
uint32_t buffer_size_;
|
||||
bool own_buffer_;
|
||||
};
|
||||
|
||||
|
||||
class PrintDisassembler: public Disassembler {
|
||||
public:
|
||||
explicit PrintDisassembler(FILE* stream) : stream_(stream) { }
|
||||
~PrintDisassembler() { }
|
||||
|
||||
protected:
|
||||
virtual void ProcessOutput(Instruction* instr);
|
||||
|
||||
private:
|
||||
FILE *stream_;
|
||||
};
|
||||
} // namespace vixl
|
||||
|
||||
#endif // VIXL_A64_DISASM_A64_H
|
|
@ -0,0 +1,238 @@
|
|||
// Copyright 2013, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "a64/instructions-a64.h"
|
||||
#include "a64/assembler-a64.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
|
||||
static uint64_t RotateRight(uint64_t value,
|
||||
unsigned int rotate,
|
||||
unsigned int width) {
|
||||
ASSERT(width <= 64);
|
||||
rotate &= 63;
|
||||
return ((value & ((1UL << rotate) - 1UL)) << (width - rotate)) |
|
||||
(value >> rotate);
|
||||
}
|
||||
|
||||
|
||||
static uint64_t RepeatBitsAcrossReg(unsigned reg_size,
|
||||
uint64_t value,
|
||||
unsigned width) {
|
||||
ASSERT((width == 2) || (width == 4) || (width == 8) || (width == 16) ||
|
||||
(width == 32));
|
||||
ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize));
|
||||
uint64_t result = value & ((1UL << width) - 1UL);
|
||||
for (unsigned i = width; i < reg_size; i *= 2) {
|
||||
result |= (result << i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Logical immediates can't encode zero, so a return value of zero is used to
|
||||
// indicate a failure case. Specifically, where the constraints on imm_s are
|
||||
// not met.
|
||||
uint64_t Instruction::ImmLogical() {
|
||||
unsigned reg_size = SixtyFourBits() ? kXRegSize : kWRegSize;
|
||||
int64_t n = BitN();
|
||||
int64_t imm_s = ImmSetBits();
|
||||
int64_t imm_r = ImmRotate();
|
||||
|
||||
// An integer is constructed from the n, imm_s and imm_r bits according to
|
||||
// the following table:
|
||||
//
|
||||
// N imms immr size S R
|
||||
// 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr)
|
||||
// 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr)
|
||||
// 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr)
|
||||
// 0 110sss xxxrrr 8 UInt(sss) UInt(rrr)
|
||||
// 0 1110ss xxxxrr 4 UInt(ss) UInt(rr)
|
||||
// 0 11110s xxxxxr 2 UInt(s) UInt(r)
|
||||
// (s bits must not be all set)
|
||||
//
|
||||
// A pattern is constructed of size bits, where the least significant S+1
|
||||
// bits are set. The pattern is rotated right by R, and repeated across a
|
||||
// 32 or 64-bit value, depending on destination register width.
|
||||
//
|
||||
|
||||
if (n == 1) {
|
||||
if (imm_s == 0x3F) {
|
||||
return 0;
|
||||
}
|
||||
uint64_t bits = (1UL << (imm_s + 1)) - 1;
|
||||
return RotateRight(bits, imm_r, 64);
|
||||
} else {
|
||||
if ((imm_s >> 1) == 0x1F) {
|
||||
return 0;
|
||||
}
|
||||
for (int width = 0x20; width >= 0x2; width >>= 1) {
|
||||
if ((imm_s & width) == 0) {
|
||||
int mask = width - 1;
|
||||
if ((imm_s & mask) == mask) {
|
||||
return 0;
|
||||
}
|
||||
uint64_t bits = (1UL << ((imm_s & mask) + 1)) - 1;
|
||||
return RepeatBitsAcrossReg(reg_size,
|
||||
RotateRight(bits, imm_r & mask, width),
|
||||
width);
|
||||
}
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
float Instruction::ImmFP32() {
|
||||
// ImmFP: abcdefgh (8 bits)
|
||||
// Single: aBbb.bbbc.defg.h000.0000.0000.0000.0000 (32 bits)
|
||||
// where B is b ^ 1
|
||||
uint32_t bits = ImmFP();
|
||||
uint32_t bit7 = (bits >> 7) & 0x1;
|
||||
uint32_t bit6 = (bits >> 6) & 0x1;
|
||||
uint32_t bit5_to_0 = bits & 0x3f;
|
||||
uint32_t result = (bit7 << 31) | ((32 - bit6) << 25) | (bit5_to_0 << 19);
|
||||
|
||||
return rawbits_to_float(result);
|
||||
}
|
||||
|
||||
|
||||
double Instruction::ImmFP64() {
|
||||
// ImmFP: abcdefgh (8 bits)
|
||||
// Double: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000
|
||||
// 0000.0000.0000.0000.0000.0000.0000.0000 (64 bits)
|
||||
// where B is b ^ 1
|
||||
uint32_t bits = ImmFP();
|
||||
uint64_t bit7 = (bits >> 7) & 0x1;
|
||||
uint64_t bit6 = (bits >> 6) & 0x1;
|
||||
uint64_t bit5_to_0 = bits & 0x3f;
|
||||
uint64_t result = (bit7 << 63) | ((256 - bit6) << 54) | (bit5_to_0 << 48);
|
||||
|
||||
return rawbits_to_double(result);
|
||||
}
|
||||
|
||||
|
||||
LSDataSize CalcLSPairDataSize(LoadStorePairOp op) {
|
||||
switch (op) {
|
||||
case STP_x:
|
||||
case LDP_x:
|
||||
case STP_d:
|
||||
case LDP_d: return LSDoubleWord;
|
||||
default: return LSWord;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Instruction* Instruction::ImmPCOffsetTarget() {
|
||||
ptrdiff_t offset;
|
||||
if (IsPCRelAddressing()) {
|
||||
// PC-relative addressing. Only ADR is supported.
|
||||
offset = ImmPCRel();
|
||||
} else {
|
||||
// All PC-relative branches.
|
||||
ASSERT(BranchType() != UnknownBranchType);
|
||||
// Relative branch offsets are instruction-size-aligned.
|
||||
offset = ImmBranch() << kInstructionSizeLog2;
|
||||
}
|
||||
return this + offset;
|
||||
}
|
||||
|
||||
|
||||
inline int Instruction::ImmBranch() const {
|
||||
switch (BranchType()) {
|
||||
case CondBranchType: return ImmCondBranch();
|
||||
case UncondBranchType: return ImmUncondBranch();
|
||||
case CompareBranchType: return ImmCmpBranch();
|
||||
case TestBranchType: return ImmTestBranch();
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void Instruction::SetImmPCOffsetTarget(Instruction* target) {
|
||||
if (IsPCRelAddressing()) {
|
||||
SetPCRelImmTarget(target);
|
||||
} else {
|
||||
SetBranchImmTarget(target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Instruction::SetPCRelImmTarget(Instruction* target) {
|
||||
// ADRP is not supported, so 'this' must point to an ADR instruction.
|
||||
ASSERT(Mask(PCRelAddressingMask) == ADR);
|
||||
|
||||
Instr imm = Assembler::ImmPCRelAddress(target - this);
|
||||
|
||||
SetInstructionBits(Mask(~ImmPCRel_mask) | imm);
|
||||
}
|
||||
|
||||
|
||||
void Instruction::SetBranchImmTarget(Instruction* target) {
|
||||
ASSERT(((target - this) & 3) == 0);
|
||||
Instr branch_imm = 0;
|
||||
uint32_t imm_mask = 0;
|
||||
int offset = (target - this) >> kInstructionSizeLog2;
|
||||
switch (BranchType()) {
|
||||
case CondBranchType: {
|
||||
branch_imm = Assembler::ImmCondBranch(offset);
|
||||
imm_mask = ImmCondBranch_mask;
|
||||
break;
|
||||
}
|
||||
case UncondBranchType: {
|
||||
branch_imm = Assembler::ImmUncondBranch(offset);
|
||||
imm_mask = ImmUncondBranch_mask;
|
||||
break;
|
||||
}
|
||||
case CompareBranchType: {
|
||||
branch_imm = Assembler::ImmCmpBranch(offset);
|
||||
imm_mask = ImmCmpBranch_mask;
|
||||
break;
|
||||
}
|
||||
case TestBranchType: {
|
||||
branch_imm = Assembler::ImmTestBranch(offset);
|
||||
imm_mask = ImmTestBranch_mask;
|
||||
break;
|
||||
}
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
SetInstructionBits(Mask(~imm_mask) | branch_imm);
|
||||
}
|
||||
|
||||
|
||||
void Instruction::SetImmLLiteral(Instruction* source) {
|
||||
ASSERT(((source - this) & 3) == 0);
|
||||
int offset = (source - this) >> kLiteralEntrySizeLog2;
|
||||
Instr imm = Assembler::ImmLLiteral(offset);
|
||||
Instr mask = ImmLLiteral_mask;
|
||||
|
||||
SetInstructionBits(Mask(~mask) | imm);
|
||||
}
|
||||
} // namespace vixl
|
||||
|
|
@ -0,0 +1,344 @@
|
|||
// Copyright 2013, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef VIXL_A64_INSTRUCTIONS_A64_H_
|
||||
#define VIXL_A64_INSTRUCTIONS_A64_H_
|
||||
|
||||
#include "globals.h"
|
||||
#include "utils.h"
|
||||
#include "a64/constants-a64.h"
|
||||
|
||||
namespace vixl {
|
||||
// ISA constants. --------------------------------------------------------------
|
||||
|
||||
typedef uint32_t Instr;
|
||||
const unsigned kInstructionSize = 4;
|
||||
const unsigned kInstructionSizeLog2 = 2;
|
||||
const unsigned kLiteralEntrySize = 4;
|
||||
const unsigned kLiteralEntrySizeLog2 = 2;
|
||||
const unsigned kMaxLoadLiteralRange = 1 * MBytes;
|
||||
|
||||
const unsigned kWRegSize = 32;
|
||||
const unsigned kWRegSizeLog2 = 5;
|
||||
const unsigned kWRegSizeInBytes = kWRegSize / 8;
|
||||
const unsigned kXRegSize = 64;
|
||||
const unsigned kXRegSizeLog2 = 6;
|
||||
const unsigned kXRegSizeInBytes = kXRegSize / 8;
|
||||
const unsigned kSRegSize = 32;
|
||||
const unsigned kSRegSizeLog2 = 5;
|
||||
const unsigned kSRegSizeInBytes = kSRegSize / 8;
|
||||
const unsigned kDRegSize = 64;
|
||||
const unsigned kDRegSizeLog2 = 6;
|
||||
const unsigned kDRegSizeInBytes = kDRegSize / 8;
|
||||
const int64_t kWRegMask = 0x00000000ffffffffLL;
|
||||
const int64_t kXRegMask = 0xffffffffffffffffLL;
|
||||
const int64_t kSRegMask = 0x00000000ffffffffLL;
|
||||
const int64_t kDRegMask = 0xffffffffffffffffLL;
|
||||
const int64_t kXSignMask = 0x1LL << 63;
|
||||
const int64_t kWSignMask = 0x1LL << 31;
|
||||
const int64_t kByteMask = 0xffL;
|
||||
const int64_t kHalfWordMask = 0xffffL;
|
||||
const int64_t kWordMask = 0xffffffffLL;
|
||||
const uint64_t kXMaxUInt = 0xffffffffffffffffULL;
|
||||
const uint64_t kWMaxUInt = 0xffffffffULL;
|
||||
const int64_t kXMaxInt = 0x7fffffffffffffffLL;
|
||||
const int64_t kXMinInt = 0x8000000000000000LL;
|
||||
const int32_t kWMaxInt = 0x7fffffff;
|
||||
const int32_t kWMinInt = 0x80000000;
|
||||
const unsigned kLinkRegCode = 30;
|
||||
const unsigned kZeroRegCode = 31;
|
||||
const unsigned kSPRegInternalCode = 63;
|
||||
const unsigned kRegCodeMask = 0x1f;
|
||||
|
||||
// AArch64 floating-point specifics. These match IEEE-754.
|
||||
const unsigned kDoubleMantissaBits = 52;
|
||||
const unsigned kDoubleExponentBits = 11;
|
||||
const unsigned kFloatMantissaBits = 23;
|
||||
const unsigned kFloatExponentBits = 8;
|
||||
|
||||
const float kFP32PositiveInfinity = rawbits_to_float(0x7f800000);
|
||||
const float kFP32NegativeInfinity = rawbits_to_float(0xff800000);
|
||||
const double kFP64PositiveInfinity = rawbits_to_double(0x7ff0000000000000ULL);
|
||||
const double kFP64NegativeInfinity = rawbits_to_double(0xfff0000000000000ULL);
|
||||
|
||||
// This value is a signalling NaN as both a double and as a float (taking the
|
||||
// least-significant word).
|
||||
static const double kFP64SignallingNaN = rawbits_to_double(0x7ff000007f800001ULL);
|
||||
static const float kFP32SignallingNaN = rawbits_to_float(0x7f800001);
|
||||
|
||||
// A similar value, but as a quiet NaN.
|
||||
static const double kFP64QuietNaN = rawbits_to_double(0x7ff800007fc00001ULL);
|
||||
static const float kFP32QuietNaN = rawbits_to_float(0x7fc00001);
|
||||
|
||||
enum LSDataSize {
|
||||
LSByte = 0,
|
||||
LSHalfword = 1,
|
||||
LSWord = 2,
|
||||
LSDoubleWord = 3
|
||||
};
|
||||
|
||||
LSDataSize CalcLSPairDataSize(LoadStorePairOp op);
|
||||
|
||||
enum ImmBranchType {
|
||||
UnknownBranchType = 0,
|
||||
CondBranchType = 1,
|
||||
UncondBranchType = 2,
|
||||
CompareBranchType = 3,
|
||||
TestBranchType = 4
|
||||
};
|
||||
|
||||
enum AddrMode {
|
||||
Offset,
|
||||
PreIndex,
|
||||
PostIndex
|
||||
};
|
||||
|
||||
enum FPRounding {
|
||||
// The first four values are encodable directly by FPCR<RMode>.
|
||||
FPTieEven = 0x0,
|
||||
FPPositiveInfinity = 0x1,
|
||||
FPNegativeInfinity = 0x2,
|
||||
FPZero = 0x3,
|
||||
|
||||
// The final rounding mode is only available when explicitly specified by the
|
||||
// instruction (such as with fcvta). It cannot be set in FPCR.
|
||||
FPTieAway
|
||||
};
|
||||
|
||||
enum Reg31Mode {
|
||||
Reg31IsStackPointer,
|
||||
Reg31IsZeroRegister
|
||||
};
|
||||
|
||||
// Instructions. ---------------------------------------------------------------
|
||||
|
||||
class Instruction {
|
||||
public:
|
||||
inline Instr InstructionBits() const {
|
||||
return *(reinterpret_cast<const Instr*>(this));
|
||||
}
|
||||
|
||||
inline void SetInstructionBits(Instr new_instr) {
|
||||
*(reinterpret_cast<Instr*>(this)) = new_instr;
|
||||
}
|
||||
|
||||
inline int Bit(int pos) const {
|
||||
return (InstructionBits() >> pos) & 1;
|
||||
}
|
||||
|
||||
inline uint32_t Bits(int msb, int lsb) const {
|
||||
return unsigned_bitextract_32(msb, lsb, InstructionBits());
|
||||
}
|
||||
|
||||
inline int32_t SignedBits(int msb, int lsb) const {
|
||||
int32_t bits = *(reinterpret_cast<const int32_t*>(this));
|
||||
return signed_bitextract_32(msb, lsb, bits);
|
||||
}
|
||||
|
||||
inline Instr Mask(uint32_t mask) const {
|
||||
return InstructionBits() & mask;
|
||||
}
|
||||
|
||||
#define DEFINE_GETTER(Name, HighBit, LowBit, Func) \
|
||||
inline int64_t Name() const { return Func(HighBit, LowBit); }
|
||||
INSTRUCTION_FIELDS_LIST(DEFINE_GETTER)
|
||||
#undef DEFINE_GETTER
|
||||
|
||||
// ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST),
|
||||
// formed from ImmPCRelLo and ImmPCRelHi.
|
||||
int ImmPCRel() const {
|
||||
int const offset = ((ImmPCRelHi() << ImmPCRelLo_width) | ImmPCRelLo());
|
||||
int const width = ImmPCRelLo_width + ImmPCRelHi_width;
|
||||
return signed_bitextract_32(width-1, 0, offset);
|
||||
}
|
||||
|
||||
uint64_t ImmLogical();
|
||||
float ImmFP32();
|
||||
double ImmFP64();
|
||||
|
||||
inline LSDataSize SizeLSPair() const {
|
||||
return CalcLSPairDataSize(
|
||||
static_cast<LoadStorePairOp>(Mask(LoadStorePairMask)));
|
||||
}
|
||||
|
||||
// Helpers.
|
||||
inline bool IsCondBranchImm() const {
|
||||
return Mask(ConditionalBranchFMask) == ConditionalBranchFixed;
|
||||
}
|
||||
|
||||
inline bool IsUncondBranchImm() const {
|
||||
return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed;
|
||||
}
|
||||
|
||||
inline bool IsCompareBranch() const {
|
||||
return Mask(CompareBranchFMask) == CompareBranchFixed;
|
||||
}
|
||||
|
||||
inline bool IsTestBranch() const {
|
||||
return Mask(TestBranchFMask) == TestBranchFixed;
|
||||
}
|
||||
|
||||
inline bool IsPCRelAddressing() const {
|
||||
return Mask(PCRelAddressingFMask) == PCRelAddressingFixed;
|
||||
}
|
||||
|
||||
inline bool IsLogicalImmediate() const {
|
||||
return Mask(LogicalImmediateFMask) == LogicalImmediateFixed;
|
||||
}
|
||||
|
||||
inline bool IsAddSubImmediate() const {
|
||||
return Mask(AddSubImmediateFMask) == AddSubImmediateFixed;
|
||||
}
|
||||
|
||||
inline bool IsAddSubExtended() const {
|
||||
return Mask(AddSubExtendedFMask) == AddSubExtendedFixed;
|
||||
}
|
||||
|
||||
inline bool IsLoadOrStore() const {
|
||||
return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed;
|
||||
}
|
||||
|
||||
inline bool IsMovn() const {
|
||||
return (Mask(MoveWideImmediateMask) == MOVN_x) ||
|
||||
(Mask(MoveWideImmediateMask) == MOVN_w);
|
||||
}
|
||||
|
||||
// Indicate whether Rd can be the stack pointer or the zero register. This
|
||||
// does not check that the instruction actually has an Rd field.
|
||||
inline Reg31Mode RdMode() const {
|
||||
// The following instructions use sp or wsp as Rd:
|
||||
// Add/sub (immediate) when not setting the flags.
|
||||
// Add/sub (extended) when not setting the flags.
|
||||
// Logical (immediate) when not setting the flags.
|
||||
// Otherwise, r31 is the zero register.
|
||||
if (IsAddSubImmediate() || IsAddSubExtended()) {
|
||||
if (Mask(AddSubSetFlagsBit)) {
|
||||
return Reg31IsZeroRegister;
|
||||
} else {
|
||||
return Reg31IsStackPointer;
|
||||
}
|
||||
}
|
||||
if (IsLogicalImmediate()) {
|
||||
// Of the logical (immediate) instructions, only ANDS (and its aliases)
|
||||
// can set the flags. The others can all write into sp.
|
||||
// Note that some logical operations are not available to
|
||||
// immediate-operand instructions, so we have to combine two masks here.
|
||||
if (Mask(LogicalImmediateMask & LogicalOpMask) == ANDS) {
|
||||
return Reg31IsZeroRegister;
|
||||
} else {
|
||||
return Reg31IsStackPointer;
|
||||
}
|
||||
}
|
||||
return Reg31IsZeroRegister;
|
||||
}
|
||||
|
||||
// Indicate whether Rn can be the stack pointer or the zero register. This
|
||||
// does not check that the instruction actually has an Rn field.
|
||||
inline Reg31Mode RnMode() const {
|
||||
// The following instructions use sp or wsp as Rn:
|
||||
// All loads and stores.
|
||||
// Add/sub (immediate).
|
||||
// Add/sub (extended).
|
||||
// Otherwise, r31 is the zero register.
|
||||
if (IsLoadOrStore() || IsAddSubImmediate() || IsAddSubExtended()) {
|
||||
return Reg31IsStackPointer;
|
||||
}
|
||||
return Reg31IsZeroRegister;
|
||||
}
|
||||
|
||||
inline ImmBranchType BranchType() const {
|
||||
if (IsCondBranchImm()) {
|
||||
return CondBranchType;
|
||||
} else if (IsUncondBranchImm()) {
|
||||
return UncondBranchType;
|
||||
} else if (IsCompareBranch()) {
|
||||
return CompareBranchType;
|
||||
} else if (IsTestBranch()) {
|
||||
return TestBranchType;
|
||||
} else {
|
||||
return UnknownBranchType;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the target of this instruction. 'this' may be a branch or a
|
||||
// PC-relative addressing instruction.
|
||||
Instruction* ImmPCOffsetTarget();
|
||||
|
||||
// Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or
|
||||
// a PC-relative addressing instruction.
|
||||
void SetImmPCOffsetTarget(Instruction* target);
|
||||
// Patch a literal load instruction to load from 'source'.
|
||||
void SetImmLLiteral(Instruction* source);
|
||||
|
||||
inline uint8_t* LiteralAddress() {
|
||||
int offset = ImmLLiteral() << kLiteralEntrySizeLog2;
|
||||
return reinterpret_cast<uint8_t*>(this) + offset;
|
||||
}
|
||||
|
||||
inline uint32_t Literal32() {
|
||||
uint32_t literal;
|
||||
memcpy(&literal, LiteralAddress(), sizeof(literal));
|
||||
|
||||
return literal;
|
||||
}
|
||||
|
||||
inline uint64_t Literal64() {
|
||||
uint64_t literal;
|
||||
memcpy(&literal, LiteralAddress(), sizeof(literal));
|
||||
|
||||
return literal;
|
||||
}
|
||||
|
||||
inline float LiteralFP32() {
|
||||
return rawbits_to_float(Literal32());
|
||||
}
|
||||
|
||||
inline double LiteralFP64() {
|
||||
return rawbits_to_double(Literal64());
|
||||
}
|
||||
|
||||
inline Instruction* NextInstruction() {
|
||||
return this + kInstructionSize;
|
||||
}
|
||||
|
||||
inline Instruction* InstructionAtOffset(int64_t offset) {
|
||||
ASSERT(IsWordAligned(this + offset));
|
||||
return this + offset;
|
||||
}
|
||||
|
||||
template<typename T> static inline Instruction* Cast(T src) {
|
||||
return reinterpret_cast<Instruction*>(src);
|
||||
}
|
||||
|
||||
private:
|
||||
inline int ImmBranch() const;
|
||||
|
||||
void SetPCRelImmTarget(Instruction* target);
|
||||
void SetBranchImmTarget(Instruction* target);
|
||||
};
|
||||
} // namespace vixl
|
||||
|
||||
#endif // VIXL_A64_INSTRUCTIONS_A64_H_
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2013, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef VIXL_GLOBALS_H
|
||||
#define VIXL_GLOBALS_H
|
||||
|
||||
// Get the standard printf format macros for C99 stdint types.
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include "platform.h"
|
||||
|
||||
|
||||
typedef uint8_t byte;
|
||||
|
||||
const int KBytes = 1024;
|
||||
const int MBytes = 1024 * KBytes;
|
||||
|
||||
#define ABORT() printf("in %s, line %i", __FILE__, __LINE__); abort()
|
||||
#ifdef DEBUG
|
||||
#define ASSERT(condition) assert(condition)
|
||||
#define CHECK(condition) ASSERT(condition)
|
||||
#define UNIMPLEMENTED() printf("UNIMPLEMENTED\t"); ABORT()
|
||||
#define UNREACHABLE() printf("UNREACHABLE\t"); ABORT()
|
||||
#else
|
||||
#define ASSERT(condition) ((void) 0)
|
||||
#define CHECK(condition) assert(condition)
|
||||
#define UNIMPLEMENTED() ((void) 0)
|
||||
#define UNREACHABLE() ((void) 0)
|
||||
#endif
|
||||
|
||||
template <typename T> inline void USE(T) {}
|
||||
|
||||
#define ALIGNMENT_EXCEPTION() printf("ALIGNMENT EXCEPTION\t"); ABORT()
|
||||
|
||||
#endif // VIXL_GLOBALS_H
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2013, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef PLATFORM_H
|
||||
#define PLATFORM_H
|
||||
|
||||
// Define platform specific functionalities.
|
||||
|
||||
namespace vixl {
|
||||
#ifdef USE_SIMULATOR
|
||||
// Currently we assume running the simulator implies running on x86 hardware.
|
||||
inline void HostBreakpoint() { asm("int3"); }
|
||||
#else
|
||||
inline void HostBreakpoint() {
|
||||
// TODO: Implement HostBreakpoint on a64.
|
||||
}
|
||||
#endif
|
||||
} // namespace vixl
|
||||
|
||||
#endif
|
|
@ -0,0 +1,120 @@
|
|||
// Copyright 2013, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "utils.h"
|
||||
#include <stdio.h>
|
||||
|
||||
namespace vixl {
|
||||
|
||||
uint32_t float_to_rawbits(float value) {
|
||||
uint32_t bits = 0;
|
||||
memcpy(&bits, &value, 4);
|
||||
return bits;
|
||||
}
|
||||
|
||||
|
||||
uint64_t double_to_rawbits(double value) {
|
||||
uint64_t bits = 0;
|
||||
memcpy(&bits, &value, 8);
|
||||
return bits;
|
||||
}
|
||||
|
||||
|
||||
float rawbits_to_float(uint32_t bits) {
|
||||
float value = 0.0;
|
||||
memcpy(&value, &bits, 4);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
double rawbits_to_double(uint64_t bits) {
|
||||
double value = 0.0;
|
||||
memcpy(&value, &bits, 8);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
int CountLeadingZeros(uint64_t value, int width) {
|
||||
ASSERT((width == 32) || (width == 64));
|
||||
int count = 0;
|
||||
uint64_t bit_test = 1UL << (width - 1);
|
||||
while ((count < width) && ((bit_test & value) == 0)) {
|
||||
count++;
|
||||
bit_test >>= 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int CountLeadingSignBits(int64_t value, int width) {
|
||||
ASSERT((width == 32) || (width == 64));
|
||||
if (value >= 0) {
|
||||
return CountLeadingZeros(value, width) - 1;
|
||||
} else {
|
||||
return CountLeadingZeros(~value, width) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int CountTrailingZeros(uint64_t value, int width) {
|
||||
ASSERT((width == 32) || (width == 64));
|
||||
int count = 0;
|
||||
while ((count < width) && (((value >> count) & 1) == 0)) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int CountSetBits(uint64_t value, int width) {
|
||||
// TODO: Other widths could be added here, as the implementation already
|
||||
// supports them.
|
||||
ASSERT((width == 32) || (width == 64));
|
||||
|
||||
// Mask out unused bits to ensure that they are not counted.
|
||||
value &= (0xffffffffffffffffUL >> (64-width));
|
||||
|
||||
// Add up the set bits.
|
||||
// The algorithm works by adding pairs of bit fields together iteratively,
|
||||
// where the size of each bit field doubles each time.
|
||||
// An example for an 8-bit value:
|
||||
// Bits: h g f e d c b a
|
||||
// \ | \ | \ | \ |
|
||||
// value = h+g f+e d+c b+a
|
||||
// \ | \ |
|
||||
// value = h+g+f+e d+c+b+a
|
||||
// \ |
|
||||
// value = h+g+f+e+d+c+b+a
|
||||
value = ((value >> 1) & 0x5555555555555555) + (value & 0x5555555555555555);
|
||||
value = ((value >> 2) & 0x3333333333333333) + (value & 0x3333333333333333);
|
||||
value = ((value >> 4) & 0x0f0f0f0f0f0f0f0f) + (value & 0x0f0f0f0f0f0f0f0f);
|
||||
value = ((value >> 8) & 0x00ff00ff00ff00ff) + (value & 0x00ff00ff00ff00ff);
|
||||
value = ((value >> 16) & 0x0000ffff0000ffff) + (value & 0x0000ffff0000ffff);
|
||||
value = ((value >> 32) & 0x00000000ffffffff) + (value & 0x00000000ffffffff);
|
||||
|
||||
return value;
|
||||
}
|
||||
} // namespace vixl
|
|
@ -0,0 +1,126 @@
|
|||
// Copyright 2013, ARM Limited
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name of ARM Limited nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef VIXL_UTILS_H
|
||||
#define VIXL_UTILS_H
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include "globals.h"
|
||||
|
||||
namespace vixl {
|
||||
|
||||
// Check number width.
|
||||
inline bool is_intn(unsigned n, int64_t x) {
|
||||
ASSERT((0 < n) && (n < 64));
|
||||
int64_t limit = 1ULL << (n - 1);
|
||||
return (-limit <= x) && (x < limit);
|
||||
}
|
||||
|
||||
inline bool is_uintn(unsigned n, int64_t x) {
|
||||
ASSERT((0 < n) && (n < 64));
|
||||
return !(x >> n);
|
||||
}
|
||||
|
||||
inline unsigned truncate_to_intn(unsigned n, int64_t x) {
|
||||
ASSERT((0 < n) && (n < 64));
|
||||
return (x & ((1ULL << n) - 1));
|
||||
}
|
||||
|
||||
#define INT_1_TO_63_LIST(V) \
|
||||
V(1) V(2) V(3) V(4) V(5) V(6) V(7) V(8) \
|
||||
V(9) V(10) V(11) V(12) V(13) V(14) V(15) V(16) \
|
||||
V(17) V(18) V(19) V(20) V(21) V(22) V(23) V(24) \
|
||||
V(25) V(26) V(27) V(28) V(29) V(30) V(31) V(32) \
|
||||
V(33) V(34) V(35) V(36) V(37) V(38) V(39) V(40) \
|
||||
V(41) V(42) V(43) V(44) V(45) V(46) V(47) V(48) \
|
||||
V(49) V(50) V(51) V(52) V(53) V(54) V(55) V(56) \
|
||||
V(57) V(58) V(59) V(60) V(61) V(62) V(63)
|
||||
|
||||
#define DECLARE_IS_INT_N(N) \
|
||||
inline bool is_int##N(int64_t x) { return is_intn(N, x); }
|
||||
#define DECLARE_IS_UINT_N(N) \
|
||||
inline bool is_uint##N(int64_t x) { return is_uintn(N, x); }
|
||||
#define DECLARE_TRUNCATE_TO_INT_N(N) \
|
||||
inline int truncate_to_int##N(int x) { return truncate_to_intn(N, x); }
|
||||
INT_1_TO_63_LIST(DECLARE_IS_INT_N)
|
||||
INT_1_TO_63_LIST(DECLARE_IS_UINT_N)
|
||||
INT_1_TO_63_LIST(DECLARE_TRUNCATE_TO_INT_N)
|
||||
#undef DECLARE_IS_INT_N
|
||||
#undef DECLARE_IS_UINT_N
|
||||
#undef DECLARE_TRUNCATE_TO_INT_N
|
||||
|
||||
// Bit field extraction.
|
||||
inline uint32_t unsigned_bitextract_32(int msb, int lsb, uint32_t x) {
|
||||
return (x >> lsb) & ((1 << (1 + msb - lsb)) - 1);
|
||||
}
|
||||
|
||||
inline uint64_t unsigned_bitextract_64(int msb, int lsb, uint64_t x) {
|
||||
return (x >> lsb) & ((static_cast<uint64_t>(1) << (1 + msb - lsb)) - 1);
|
||||
}
|
||||
|
||||
inline int32_t signed_bitextract_32(int msb, int lsb, int32_t x) {
|
||||
return (x << (31 - msb)) >> (lsb + 31 - msb);
|
||||
}
|
||||
|
||||
inline int64_t signed_bitextract_64(int msb, int lsb, int64_t x) {
|
||||
return (x << (63 - msb)) >> (lsb + 63 - msb);
|
||||
}
|
||||
|
||||
// floating point representation
|
||||
uint32_t float_to_rawbits(float value);
|
||||
uint64_t double_to_rawbits(double value);
|
||||
float rawbits_to_float(uint32_t bits);
|
||||
double rawbits_to_double(uint64_t bits);
|
||||
|
||||
// Bits counting.
|
||||
int CountLeadingZeros(uint64_t value, int width);
|
||||
int CountLeadingSignBits(int64_t value, int width);
|
||||
int CountTrailingZeros(uint64_t value, int width);
|
||||
int CountSetBits(uint64_t value, int width);
|
||||
|
||||
// Pointer alignment
|
||||
// TODO: rename/refactor to make it specific to instructions.
|
||||
template<typename T>
|
||||
bool IsWordAligned(T pointer) {
|
||||
ASSERT(sizeof(pointer) == sizeof(intptr_t)); // NOLINT(runtime/sizeof)
|
||||
return (reinterpret_cast<intptr_t>(pointer) & 3) == 0;
|
||||
}
|
||||
|
||||
// Increment a pointer until it has the specified alignment.
|
||||
template<class T>
|
||||
T AlignUp(T pointer, size_t alignment) {
|
||||
ASSERT(sizeof(pointer) == sizeof(uintptr_t));
|
||||
uintptr_t pointer_raw = reinterpret_cast<uintptr_t>(pointer);
|
||||
size_t align_step = (alignment - pointer_raw) % alignment;
|
||||
ASSERT((pointer_raw + align_step) % alignment == 0);
|
||||
return reinterpret_cast<T>(pointer_raw + align_step);
|
||||
}
|
||||
|
||||
|
||||
} // namespace vixl
|
||||
|
||||
#endif // VIXL_UTILS_H
|
|
@ -31,6 +31,13 @@ static void aw_a10_init(Object *obj)
|
|||
|
||||
object_initialize(&s->timer, sizeof(s->timer), TYPE_AW_A10_PIT);
|
||||
qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default());
|
||||
|
||||
object_initialize(&s->emac, sizeof(s->emac), TYPE_AW_EMAC);
|
||||
qdev_set_parent_bus(DEVICE(&s->emac), sysbus_get_default());
|
||||
if (nd_table[0].used) {
|
||||
qemu_check_nic_model(&nd_table[0], TYPE_AW_EMAC);
|
||||
qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]);
|
||||
}
|
||||
}
|
||||
|
||||
static void aw_a10_realize(DeviceState *dev, Error **errp)
|
||||
|
@ -76,6 +83,15 @@ static void aw_a10_realize(DeviceState *dev, Error **errp)
|
|||
sysbus_connect_irq(sysbusdev, 4, s->irq[67]);
|
||||
sysbus_connect_irq(sysbusdev, 5, s->irq[68]);
|
||||
|
||||
object_property_set_bool(OBJECT(&s->emac), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbusdev = SYS_BUS_DEVICE(&s->emac);
|
||||
sysbus_mmio_map(sysbusdev, 0, AW_A10_EMAC_BASE);
|
||||
sysbus_connect_irq(sysbusdev, 0, s->irq[55]);
|
||||
|
||||
serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1],
|
||||
115200, serial_hds[0], DEVICE_NATIVE_ENDIAN);
|
||||
}
|
||||
|
|
|
@ -36,10 +36,17 @@ static void cubieboard_init(QEMUMachineInitArgs *args)
|
|||
Error *err = NULL;
|
||||
|
||||
s->a10 = AW_A10(object_new(TYPE_AW_A10));
|
||||
|
||||
object_property_set_int(OBJECT(&s->a10->emac), 1, "phy-addr", &err);
|
||||
if (err != NULL) {
|
||||
error_report("Couldn't set phy address: %s", error_get_pretty(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(s->a10), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_report("Couldn't realize Allwinner A10: %s\n",
|
||||
error_get_pretty(err));
|
||||
error_report("Couldn't realize Allwinner A10: %s",
|
||||
error_get_pretty(err));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ void gic_update(GICState *s)
|
|||
best_prio = 0x100;
|
||||
best_irq = 1023;
|
||||
for (irq = 0; irq < s->num_irq; irq++) {
|
||||
if (GIC_TEST_ENABLED(irq, cm) && GIC_TEST_PENDING(irq, cm)) {
|
||||
if (GIC_TEST_ENABLED(irq, cm) && gic_test_pending(s, irq, cm)) {
|
||||
if (GIC_GET_PRIORITY(irq, cpu) < best_prio) {
|
||||
best_prio = GIC_GET_PRIORITY(irq, cpu);
|
||||
best_irq = irq;
|
||||
|
@ -89,14 +89,43 @@ void gic_set_pending_private(GICState *s, int cpu, int irq)
|
|||
{
|
||||
int cm = 1 << cpu;
|
||||
|
||||
if (GIC_TEST_PENDING(irq, cm))
|
||||
if (gic_test_pending(s, irq, cm)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DPRINTF("Set %d pending cpu %d\n", irq, cpu);
|
||||
GIC_SET_PENDING(irq, cm);
|
||||
gic_update(s);
|
||||
}
|
||||
|
||||
static void gic_set_irq_11mpcore(GICState *s, int irq, int level,
|
||||
int cm, int target)
|
||||
{
|
||||
if (level) {
|
||||
GIC_SET_LEVEL(irq, cm);
|
||||
if (GIC_TEST_EDGE_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) {
|
||||
DPRINTF("Set %d pending mask %x\n", irq, target);
|
||||
GIC_SET_PENDING(irq, target);
|
||||
}
|
||||
} else {
|
||||
GIC_CLEAR_LEVEL(irq, cm);
|
||||
}
|
||||
}
|
||||
|
||||
static void gic_set_irq_generic(GICState *s, int irq, int level,
|
||||
int cm, int target)
|
||||
{
|
||||
if (level) {
|
||||
GIC_SET_LEVEL(irq, cm);
|
||||
DPRINTF("Set %d pending mask %x\n", irq, target);
|
||||
if (GIC_TEST_EDGE_TRIGGER(irq)) {
|
||||
GIC_SET_PENDING(irq, target);
|
||||
}
|
||||
} else {
|
||||
GIC_CLEAR_LEVEL(irq, cm);
|
||||
}
|
||||
}
|
||||
|
||||
/* Process a change in an external IRQ input. */
|
||||
static void gic_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
|
@ -122,19 +151,18 @@ static void gic_set_irq(void *opaque, int irq, int level)
|
|||
target = cm;
|
||||
}
|
||||
|
||||
assert(irq >= GIC_NR_SGIS);
|
||||
|
||||
if (level == GIC_TEST_LEVEL(irq, cm)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (level) {
|
||||
GIC_SET_LEVEL(irq, cm);
|
||||
if (GIC_TEST_EDGE_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) {
|
||||
DPRINTF("Set %d pending mask %x\n", irq, target);
|
||||
GIC_SET_PENDING(irq, target);
|
||||
}
|
||||
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
|
||||
gic_set_irq_11mpcore(s, irq, level, cm, target);
|
||||
} else {
|
||||
GIC_CLEAR_LEVEL(irq, cm);
|
||||
gic_set_irq_generic(s, irq, level, cm, target);
|
||||
}
|
||||
|
||||
gic_update(s);
|
||||
}
|
||||
|
||||
|
@ -151,21 +179,48 @@ static void gic_set_running_irq(GICState *s, int cpu, int irq)
|
|||
|
||||
uint32_t gic_acknowledge_irq(GICState *s, int cpu)
|
||||
{
|
||||
int new_irq;
|
||||
int ret, irq, src;
|
||||
int cm = 1 << cpu;
|
||||
new_irq = s->current_pending[cpu];
|
||||
if (new_irq == 1023
|
||||
|| GIC_GET_PRIORITY(new_irq, cpu) >= s->running_priority[cpu]) {
|
||||
irq = s->current_pending[cpu];
|
||||
if (irq == 1023
|
||||
|| GIC_GET_PRIORITY(irq, cpu) >= s->running_priority[cpu]) {
|
||||
DPRINTF("ACK no pending IRQ\n");
|
||||
return 1023;
|
||||
}
|
||||
s->last_active[new_irq][cpu] = s->running_irq[cpu];
|
||||
/* Clear pending flags for both level and edge triggered interrupts.
|
||||
Level triggered IRQs will be reasserted once they become inactive. */
|
||||
GIC_CLEAR_PENDING(new_irq, GIC_TEST_MODEL(new_irq) ? ALL_CPU_MASK : cm);
|
||||
gic_set_running_irq(s, cpu, new_irq);
|
||||
DPRINTF("ACK %d\n", new_irq);
|
||||
return new_irq;
|
||||
s->last_active[irq][cpu] = s->running_irq[cpu];
|
||||
|
||||
if (s->revision == REV_11MPCORE) {
|
||||
/* Clear pending flags for both level and edge triggered interrupts.
|
||||
* Level triggered IRQs will be reasserted once they become inactive.
|
||||
*/
|
||||
GIC_CLEAR_PENDING(irq, GIC_TEST_MODEL(irq) ? ALL_CPU_MASK : cm);
|
||||
ret = irq;
|
||||
} else {
|
||||
if (irq < GIC_NR_SGIS) {
|
||||
/* Lookup the source CPU for the SGI and clear this in the
|
||||
* sgi_pending map. Return the src and clear the overall pending
|
||||
* state on this CPU if the SGI is not pending from any CPUs.
|
||||
*/
|
||||
assert(s->sgi_pending[irq][cpu] != 0);
|
||||
src = ctz32(s->sgi_pending[irq][cpu]);
|
||||
s->sgi_pending[irq][cpu] &= ~(1 << src);
|
||||
if (s->sgi_pending[irq][cpu] == 0) {
|
||||
GIC_CLEAR_PENDING(irq, GIC_TEST_MODEL(irq) ? ALL_CPU_MASK : cm);
|
||||
}
|
||||
ret = irq | ((src & 0x7) << 10);
|
||||
} else {
|
||||
/* Clear pending state for both level and edge triggered
|
||||
* interrupts. (level triggered interrupts with an active line
|
||||
* remain pending, see gic_test_pending)
|
||||
*/
|
||||
GIC_CLEAR_PENDING(irq, GIC_TEST_MODEL(irq) ? ALL_CPU_MASK : cm);
|
||||
ret = irq;
|
||||
}
|
||||
}
|
||||
|
||||
gic_set_running_irq(s, cpu, irq);
|
||||
DPRINTF("ACK %d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val)
|
||||
|
@ -195,14 +250,18 @@ void gic_complete_irq(GICState *s, int cpu, int irq)
|
|||
}
|
||||
if (s->running_irq[cpu] == 1023)
|
||||
return; /* No active IRQ. */
|
||||
/* Mark level triggered interrupts as pending if they are still
|
||||
raised. */
|
||||
if (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm)
|
||||
&& GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
|
||||
DPRINTF("Set %d pending mask %x\n", irq, cm);
|
||||
GIC_SET_PENDING(irq, cm);
|
||||
update = 1;
|
||||
|
||||
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
|
||||
/* Mark level triggered interrupts as pending if they are still
|
||||
raised. */
|
||||
if (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm)
|
||||
&& GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
|
||||
DPRINTF("Set %d pending mask %x\n", irq, cm);
|
||||
GIC_SET_PENDING(irq, cm);
|
||||
update = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (irq != s->running_irq[cpu]) {
|
||||
/* Complete an IRQ that is not currently running. */
|
||||
int tmp = s->running_irq[cpu];
|
||||
|
@ -273,7 +332,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset)
|
|||
res = 0;
|
||||
mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (GIC_TEST_PENDING(irq + i, mask)) {
|
||||
if (gic_test_pending(s, irq + i, mask)) {
|
||||
res |= (1 << i);
|
||||
}
|
||||
}
|
||||
|
@ -323,6 +382,22 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset)
|
|||
if (GIC_TEST_EDGE_TRIGGER(irq + i))
|
||||
res |= (2 << (i * 2));
|
||||
}
|
||||
} else if (offset < 0xf10) {
|
||||
goto bad_reg;
|
||||
} else if (offset < 0xf30) {
|
||||
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
|
||||
goto bad_reg;
|
||||
}
|
||||
|
||||
if (offset < 0xf20) {
|
||||
/* GICD_CPENDSGIRn */
|
||||
irq = (offset - 0xf10);
|
||||
} else {
|
||||
irq = (offset - 0xf20);
|
||||
/* GICD_SPENDSGIRn */
|
||||
}
|
||||
|
||||
res = s->sgi_pending[irq][cpu];
|
||||
} else if (offset < 0xfe0) {
|
||||
goto bad_reg;
|
||||
} else /* offset >= 0xfe0 */ {
|
||||
|
@ -497,9 +572,31 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
|
|||
GIC_CLEAR_EDGE_TRIGGER(irq + i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if (offset < 0xf10) {
|
||||
/* 0xf00 is only handled for 32-bit writes. */
|
||||
goto bad_reg;
|
||||
} else if (offset < 0xf20) {
|
||||
/* GICD_CPENDSGIRn */
|
||||
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
|
||||
goto bad_reg;
|
||||
}
|
||||
irq = (offset - 0xf10);
|
||||
|
||||
s->sgi_pending[irq][cpu] &= ~value;
|
||||
if (s->sgi_pending[irq][cpu] == 0) {
|
||||
GIC_CLEAR_PENDING(irq, 1 << cpu);
|
||||
}
|
||||
} else if (offset < 0xf30) {
|
||||
/* GICD_SPENDSGIRn */
|
||||
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
|
||||
goto bad_reg;
|
||||
}
|
||||
irq = (offset - 0xf20);
|
||||
|
||||
GIC_SET_PENDING(irq, 1 << cpu);
|
||||
s->sgi_pending[irq][cpu] |= value;
|
||||
} else {
|
||||
goto bad_reg;
|
||||
}
|
||||
gic_update(s);
|
||||
return;
|
||||
|
@ -523,6 +620,7 @@ static void gic_dist_writel(void *opaque, hwaddr offset,
|
|||
int cpu;
|
||||
int irq;
|
||||
int mask;
|
||||
int target_cpu;
|
||||
|
||||
cpu = gic_get_current_cpu(s);
|
||||
irq = value & 0x3ff;
|
||||
|
@ -542,6 +640,12 @@ static void gic_dist_writel(void *opaque, hwaddr offset,
|
|||
break;
|
||||
}
|
||||
GIC_SET_PENDING(irq, mask);
|
||||
target_cpu = ctz32(mask);
|
||||
while (target_cpu < GIC_NCPU) {
|
||||
s->sgi_pending[irq][target_cpu] |= (1 << cpu);
|
||||
mask &= ~(1 << target_cpu);
|
||||
target_cpu = ctz32(mask);
|
||||
}
|
||||
gic_update(s);
|
||||
return;
|
||||
}
|
||||
|
@ -565,14 +669,17 @@ static uint32_t gic_cpu_read(GICState *s, int cpu, int offset)
|
|||
case 0x04: /* Priority mask */
|
||||
return s->priority_mask[cpu];
|
||||
case 0x08: /* Binary Point */
|
||||
/* ??? Not implemented. */
|
||||
return 0;
|
||||
return s->bpr[cpu];
|
||||
case 0x0c: /* Acknowledge */
|
||||
return gic_acknowledge_irq(s, cpu);
|
||||
case 0x14: /* Running Priority */
|
||||
return s->running_priority[cpu];
|
||||
case 0x18: /* Highest Pending Interrupt */
|
||||
return s->current_pending[cpu];
|
||||
case 0x1c: /* Aliased Binary Point */
|
||||
return s->abpr[cpu];
|
||||
case 0xd0: case 0xd4: case 0xd8: case 0xdc:
|
||||
return s->apr[(offset - 0xd0) / 4][cpu];
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"gic_cpu_read: Bad offset %x\n", (int)offset);
|
||||
|
@ -591,10 +698,18 @@ static void gic_cpu_write(GICState *s, int cpu, int offset, uint32_t value)
|
|||
s->priority_mask[cpu] = (value & 0xff);
|
||||
break;
|
||||
case 0x08: /* Binary Point */
|
||||
/* ??? Not implemented. */
|
||||
s->bpr[cpu] = (value & 0x7);
|
||||
break;
|
||||
case 0x10: /* End Of Interrupt */
|
||||
return gic_complete_irq(s, cpu, value & 0x3ff);
|
||||
case 0x1c: /* Aliased Binary Point */
|
||||
if (s->revision >= 2) {
|
||||
s->abpr[cpu] = (value & 0x7);
|
||||
}
|
||||
break;
|
||||
case 0xd0: case 0xd4: case 0xd8: case 0xdc:
|
||||
qemu_log_mask(LOG_UNIMP, "Writing APR not implemented\n");
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"gic_cpu_write: Bad offset %x\n", (int)offset);
|
||||
|
|
|
@ -58,8 +58,8 @@ static const VMStateDescription vmstate_gic_irq_state = {
|
|||
|
||||
static const VMStateDescription vmstate_gic = {
|
||||
.name = "arm_gic",
|
||||
.version_id = 4,
|
||||
.minimum_version_id = 4,
|
||||
.version_id = 7,
|
||||
.minimum_version_id = 7,
|
||||
.pre_save = gic_pre_save,
|
||||
.post_load = gic_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
|
@ -71,10 +71,14 @@ static const VMStateDescription vmstate_gic = {
|
|||
VMSTATE_UINT8_2DARRAY(priority1, GICState, GIC_INTERNAL, GIC_NCPU),
|
||||
VMSTATE_UINT8_ARRAY(priority2, GICState, GIC_MAXIRQ - GIC_INTERNAL),
|
||||
VMSTATE_UINT16_2DARRAY(last_active, GICState, GIC_MAXIRQ, GIC_NCPU),
|
||||
VMSTATE_UINT8_2DARRAY(sgi_pending, GICState, GIC_NR_SGIS, GIC_NCPU),
|
||||
VMSTATE_UINT16_ARRAY(priority_mask, GICState, GIC_NCPU),
|
||||
VMSTATE_UINT16_ARRAY(running_irq, GICState, GIC_NCPU),
|
||||
VMSTATE_UINT16_ARRAY(running_priority, GICState, GIC_NCPU),
|
||||
VMSTATE_UINT16_ARRAY(current_pending, GICState, GIC_NCPU),
|
||||
VMSTATE_UINT8_ARRAY(bpr, GICState, GIC_NCPU),
|
||||
VMSTATE_UINT8_ARRAY(abpr, GICState, GIC_NCPU),
|
||||
VMSTATE_UINT32_2DARRAY(apr, GICState, GIC_NR_APRS, GIC_NCPU),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
#define GIC_TEST_ENABLED(irq, cm) ((s->irq_state[irq].enabled & (cm)) != 0)
|
||||
#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm)
|
||||
#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm)
|
||||
#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0)
|
||||
#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm)
|
||||
#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm)
|
||||
#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0)
|
||||
|
@ -63,4 +62,19 @@ void gic_update(GICState *s);
|
|||
void gic_init_irqs_and_distributor(GICState *s, int num_irq);
|
||||
void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val);
|
||||
|
||||
static inline bool gic_test_pending(GICState *s, int irq, int cm)
|
||||
{
|
||||
if (s->revision == REV_NVIC || s->revision == REV_11MPCORE) {
|
||||
return s->irq_state[irq].pending & cm;
|
||||
} else {
|
||||
/* Edge-triggered interrupts are marked pending on a rising edge, but
|
||||
* level-triggered interrupts are either considered pending when the
|
||||
* level is active or if software has explicitly written to
|
||||
* GICD_ISPENDR to set the state pending.
|
||||
*/
|
||||
return (s->irq_state[irq].pending & cm) ||
|
||||
(!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_LEVEL(irq, cm));
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* !QEMU_ARM_GIC_INTERNAL_H */
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#define XILINX_LOCK_KEY 0x767b
|
||||
#define XILINX_UNLOCK_KEY 0xdf0d
|
||||
|
||||
#define R_PSS_RST_CTRL_SOFT_RST 0x1
|
||||
|
||||
typedef enum {
|
||||
ARM_PLL_CTRL,
|
||||
DDR_PLL_CTRL,
|
||||
|
@ -399,6 +401,9 @@ static void zynq_slcr_write(void *opaque, hwaddr offset,
|
|||
goto bad_reg;
|
||||
}
|
||||
s->reset[(offset - 0x200) / 4] = val;
|
||||
if (offset == 0x200 && (val & R_PSS_RST_CTRL_SOFT_RST)) {
|
||||
qemu_system_reset_request();
|
||||
}
|
||||
break;
|
||||
case 0x300:
|
||||
s->apu_ctrl = val;
|
||||
|
|
|
@ -18,6 +18,7 @@ common-obj-$(CONFIG_OPENCORES_ETH) += opencores_eth.o
|
|||
common-obj-$(CONFIG_XGMAC) += xgmac.o
|
||||
common-obj-$(CONFIG_MIPSNET) += mipsnet.o
|
||||
common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
|
||||
common-obj-$(CONFIG_ALLWINNER_EMAC) += allwinner_emac.o
|
||||
|
||||
common-obj-$(CONFIG_CADENCE) += cadence_gem.o
|
||||
common-obj-$(CONFIG_STELLARIS_ENET) += stellaris_enet.o
|
||||
|
|
|
@ -0,0 +1,539 @@
|
|||
/*
|
||||
* Emulation of Allwinner EMAC Fast Ethernet controller and
|
||||
* Realtek RTL8201CP PHY
|
||||
*
|
||||
* Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
|
||||
*
|
||||
* This model is based on reverse-engineering of Linux kernel driver.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
#include "hw/sysbus.h"
|
||||
#include "net/net.h"
|
||||
#include "qemu/fifo8.h"
|
||||
#include "hw/net/allwinner_emac.h"
|
||||
#include <zlib.h>
|
||||
|
||||
static uint8_t padding[60];
|
||||
|
||||
static void mii_set_link(RTL8201CPState *mii, bool link_ok)
|
||||
{
|
||||
if (link_ok) {
|
||||
mii->bmsr |= MII_BMSR_LINK_ST;
|
||||
mii->anlpar |= MII_ANAR_TXFD | MII_ANAR_10FD | MII_ANAR_10 |
|
||||
MII_ANAR_CSMACD;
|
||||
} else {
|
||||
mii->bmsr &= ~MII_BMSR_LINK_ST;
|
||||
mii->anlpar = MII_ANAR_TX;
|
||||
}
|
||||
}
|
||||
|
||||
static void mii_reset(RTL8201CPState *mii, bool link_ok)
|
||||
{
|
||||
mii->bmcr = MII_BMCR_FD | MII_BMCR_AUTOEN | MII_BMCR_SPEED;
|
||||
mii->bmsr = MII_BMSR_100TX_FD | MII_BMSR_100TX_HD | MII_BMSR_10T_FD |
|
||||
MII_BMSR_10T_HD | MII_BMSR_MFPS | MII_BMSR_AUTONEG;
|
||||
mii->anar = MII_ANAR_TXFD | MII_ANAR_TX | MII_ANAR_10FD | MII_ANAR_10 |
|
||||
MII_ANAR_CSMACD;
|
||||
mii->anlpar = MII_ANAR_TX;
|
||||
|
||||
mii_set_link(mii, link_ok);
|
||||
}
|
||||
|
||||
static uint16_t RTL8201CP_mdio_read(AwEmacState *s, uint8_t addr, uint8_t reg)
|
||||
{
|
||||
RTL8201CPState *mii = &s->mii;
|
||||
uint16_t ret = 0xffff;
|
||||
|
||||
if (addr == s->phy_addr) {
|
||||
switch (reg) {
|
||||
case MII_BMCR:
|
||||
return mii->bmcr;
|
||||
case MII_BMSR:
|
||||
return mii->bmsr;
|
||||
case MII_PHYID1:
|
||||
return RTL8201CP_PHYID1;
|
||||
case MII_PHYID2:
|
||||
return RTL8201CP_PHYID2;
|
||||
case MII_ANAR:
|
||||
return mii->anar;
|
||||
case MII_ANLPAR:
|
||||
return mii->anlpar;
|
||||
case MII_ANER:
|
||||
case MII_NSR:
|
||||
case MII_LBREMR:
|
||||
case MII_REC:
|
||||
case MII_SNRDR:
|
||||
case MII_TEST:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"allwinner_emac: read from unimpl. mii reg 0x%x\n",
|
||||
reg);
|
||||
return 0;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"allwinner_emac: read from invalid mii reg 0x%x\n",
|
||||
reg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void RTL8201CP_mdio_write(AwEmacState *s, uint8_t addr, uint8_t reg,
|
||||
uint16_t value)
|
||||
{
|
||||
RTL8201CPState *mii = &s->mii;
|
||||
NetClientState *nc;
|
||||
|
||||
if (addr == s->phy_addr) {
|
||||
switch (reg) {
|
||||
case MII_BMCR:
|
||||
if (value & MII_BMCR_RESET) {
|
||||
nc = qemu_get_queue(s->nic);
|
||||
mii_reset(mii, !nc->link_down);
|
||||
} else {
|
||||
mii->bmcr = value;
|
||||
}
|
||||
break;
|
||||
case MII_ANAR:
|
||||
mii->anar = value;
|
||||
break;
|
||||
case MII_BMSR:
|
||||
case MII_PHYID1:
|
||||
case MII_PHYID2:
|
||||
case MII_ANLPAR:
|
||||
case MII_ANER:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"allwinner_emac: write to read-only mii reg 0x%x\n",
|
||||
reg);
|
||||
break;
|
||||
case MII_NSR:
|
||||
case MII_LBREMR:
|
||||
case MII_REC:
|
||||
case MII_SNRDR:
|
||||
case MII_TEST:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"allwinner_emac: write to unimpl. mii reg 0x%x\n",
|
||||
reg);
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"allwinner_emac: write to invalid mii reg 0x%x\n",
|
||||
reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void aw_emac_update_irq(AwEmacState *s)
|
||||
{
|
||||
qemu_set_irq(s->irq, (s->int_sta & s->int_ctl) != 0);
|
||||
}
|
||||
|
||||
static void aw_emac_tx_reset(AwEmacState *s, int chan)
|
||||
{
|
||||
fifo8_reset(&s->tx_fifo[chan]);
|
||||
s->tx_length[chan] = 0;
|
||||
}
|
||||
|
||||
static void aw_emac_rx_reset(AwEmacState *s)
|
||||
{
|
||||
fifo8_reset(&s->rx_fifo);
|
||||
s->rx_num_packets = 0;
|
||||
s->rx_packet_size = 0;
|
||||
s->rx_packet_pos = 0;
|
||||
}
|
||||
|
||||
static void fifo8_push_word(Fifo8 *fifo, uint32_t val)
|
||||
{
|
||||
fifo8_push(fifo, val);
|
||||
fifo8_push(fifo, val >> 8);
|
||||
fifo8_push(fifo, val >> 16);
|
||||
fifo8_push(fifo, val >> 24);
|
||||
}
|
||||
|
||||
static uint32_t fifo8_pop_word(Fifo8 *fifo)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
ret = fifo8_pop(fifo);
|
||||
ret |= fifo8_pop(fifo) << 8;
|
||||
ret |= fifo8_pop(fifo) << 16;
|
||||
ret |= fifo8_pop(fifo) << 24;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int aw_emac_can_receive(NetClientState *nc)
|
||||
{
|
||||
AwEmacState *s = qemu_get_nic_opaque(nc);
|
||||
|
||||
/*
|
||||
* To avoid packet drops, allow reception only when there is space
|
||||
* for a full frame: 1522 + 8 (rx headers) + 2 (padding).
|
||||
*/
|
||||
return (s->ctl & EMAC_CTL_RX_EN) && (fifo8_num_free(&s->rx_fifo) >= 1532);
|
||||
}
|
||||
|
||||
static ssize_t aw_emac_receive(NetClientState *nc, const uint8_t *buf,
|
||||
size_t size)
|
||||
{
|
||||
AwEmacState *s = qemu_get_nic_opaque(nc);
|
||||
Fifo8 *fifo = &s->rx_fifo;
|
||||
size_t padded_size, total_size;
|
||||
uint32_t crc;
|
||||
|
||||
padded_size = size > 60 ? size : 60;
|
||||
total_size = QEMU_ALIGN_UP(RX_HDR_SIZE + padded_size + CRC_SIZE, 4);
|
||||
|
||||
if (!(s->ctl & EMAC_CTL_RX_EN) || (fifo8_num_free(fifo) < total_size)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fifo8_push_word(fifo, EMAC_UNDOCUMENTED_MAGIC);
|
||||
fifo8_push_word(fifo, EMAC_RX_HEADER(padded_size + CRC_SIZE,
|
||||
EMAC_RX_IO_DATA_STATUS_OK));
|
||||
fifo8_push_all(fifo, buf, size);
|
||||
crc = crc32(~0, buf, size);
|
||||
|
||||
if (padded_size != size) {
|
||||
fifo8_push_all(fifo, padding, padded_size - size);
|
||||
crc = crc32(crc, padding, padded_size - size);
|
||||
}
|
||||
|
||||
fifo8_push_word(fifo, crc);
|
||||
fifo8_push_all(fifo, padding, QEMU_ALIGN_UP(padded_size, 4) - padded_size);
|
||||
s->rx_num_packets++;
|
||||
|
||||
s->int_sta |= EMAC_INT_RX;
|
||||
aw_emac_update_irq(s);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void aw_emac_cleanup(NetClientState *nc)
|
||||
{
|
||||
AwEmacState *s = qemu_get_nic_opaque(nc);
|
||||
|
||||
s->nic = NULL;
|
||||
}
|
||||
|
||||
static void aw_emac_reset(DeviceState *dev)
|
||||
{
|
||||
AwEmacState *s = AW_EMAC(dev);
|
||||
NetClientState *nc = qemu_get_queue(s->nic);
|
||||
|
||||
s->ctl = 0;
|
||||
s->tx_mode = 0;
|
||||
s->int_ctl = 0;
|
||||
s->int_sta = 0;
|
||||
s->tx_channel = 0;
|
||||
s->phy_target = 0;
|
||||
|
||||
aw_emac_tx_reset(s, 0);
|
||||
aw_emac_tx_reset(s, 1);
|
||||
aw_emac_rx_reset(s);
|
||||
|
||||
mii_reset(&s->mii, !nc->link_down);
|
||||
}
|
||||
|
||||
static uint64_t aw_emac_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
AwEmacState *s = opaque;
|
||||
Fifo8 *fifo = &s->rx_fifo;
|
||||
NetClientState *nc;
|
||||
uint64_t ret;
|
||||
|
||||
switch (offset) {
|
||||
case EMAC_CTL_REG:
|
||||
return s->ctl;
|
||||
case EMAC_TX_MODE_REG:
|
||||
return s->tx_mode;
|
||||
case EMAC_TX_INS_REG:
|
||||
return s->tx_channel;
|
||||
case EMAC_RX_CTL_REG:
|
||||
return s->rx_ctl;
|
||||
case EMAC_RX_IO_DATA_REG:
|
||||
if (!s->rx_num_packets) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"Read IO data register when no packet available");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = fifo8_pop_word(fifo);
|
||||
|
||||
switch (s->rx_packet_pos) {
|
||||
case 0: /* Word is magic header */
|
||||
s->rx_packet_pos += 4;
|
||||
break;
|
||||
case 4: /* Word is rx info header */
|
||||
s->rx_packet_pos += 4;
|
||||
s->rx_packet_size = QEMU_ALIGN_UP(extract32(ret, 0, 16), 4);
|
||||
break;
|
||||
default: /* Word is packet data */
|
||||
s->rx_packet_pos += 4;
|
||||
s->rx_packet_size -= 4;
|
||||
|
||||
if (!s->rx_packet_size) {
|
||||
s->rx_packet_pos = 0;
|
||||
s->rx_num_packets--;
|
||||
nc = qemu_get_queue(s->nic);
|
||||
if (aw_emac_can_receive(nc)) {
|
||||
qemu_flush_queued_packets(nc);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
case EMAC_RX_FBC_REG:
|
||||
return s->rx_num_packets;
|
||||
case EMAC_INT_CTL_REG:
|
||||
return s->int_ctl;
|
||||
case EMAC_INT_STA_REG:
|
||||
return s->int_sta;
|
||||
case EMAC_MAC_MRDD_REG:
|
||||
return RTL8201CP_mdio_read(s,
|
||||
extract32(s->phy_target, PHY_ADDR_SHIFT, 8),
|
||||
extract32(s->phy_target, PHY_REG_SHIFT, 8));
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"allwinner_emac: read access to unknown register 0x"
|
||||
TARGET_FMT_plx "\n", offset);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void aw_emac_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
AwEmacState *s = opaque;
|
||||
Fifo8 *fifo;
|
||||
NetClientState *nc = qemu_get_queue(s->nic);
|
||||
int chan;
|
||||
|
||||
switch (offset) {
|
||||
case EMAC_CTL_REG:
|
||||
if (value & EMAC_CTL_RESET) {
|
||||
aw_emac_reset(DEVICE(s));
|
||||
value &= ~EMAC_CTL_RESET;
|
||||
}
|
||||
s->ctl = value;
|
||||
if (aw_emac_can_receive(nc)) {
|
||||
qemu_flush_queued_packets(nc);
|
||||
}
|
||||
break;
|
||||
case EMAC_TX_MODE_REG:
|
||||
s->tx_mode = value;
|
||||
break;
|
||||
case EMAC_TX_CTL0_REG:
|
||||
case EMAC_TX_CTL1_REG:
|
||||
chan = (offset == EMAC_TX_CTL0_REG ? 0 : 1);
|
||||
if ((value & 1) && (s->ctl & EMAC_CTL_TX_EN)) {
|
||||
uint32_t len, ret;
|
||||
const uint8_t *data;
|
||||
|
||||
fifo = &s->tx_fifo[chan];
|
||||
len = s->tx_length[chan];
|
||||
|
||||
if (len > fifo8_num_used(fifo)) {
|
||||
len = fifo8_num_used(fifo);
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"allwinner_emac: TX length > fifo data length\n");
|
||||
}
|
||||
if (len > 0) {
|
||||
data = fifo8_pop_buf(fifo, len, &ret);
|
||||
qemu_send_packet(nc, data, ret);
|
||||
aw_emac_tx_reset(s, chan);
|
||||
/* Raise TX interrupt */
|
||||
s->int_sta |= EMAC_INT_TX_CHAN(chan);
|
||||
aw_emac_update_irq(s);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EMAC_TX_INS_REG:
|
||||
s->tx_channel = value < NUM_TX_FIFOS ? value : 0;
|
||||
break;
|
||||
case EMAC_TX_PL0_REG:
|
||||
case EMAC_TX_PL1_REG:
|
||||
chan = (offset == EMAC_TX_PL0_REG ? 0 : 1);
|
||||
if (value > TX_FIFO_SIZE) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"allwinner_emac: invalid TX frame length %d\n",
|
||||
(int)value);
|
||||
value = TX_FIFO_SIZE;
|
||||
}
|
||||
s->tx_length[chan] = value;
|
||||
break;
|
||||
case EMAC_TX_IO_DATA_REG:
|
||||
fifo = &s->tx_fifo[s->tx_channel];
|
||||
if (fifo8_num_free(fifo) < 4) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"allwinner_emac: TX data overruns fifo\n");
|
||||
break;
|
||||
}
|
||||
fifo8_push_word(fifo, value);
|
||||
break;
|
||||
case EMAC_RX_CTL_REG:
|
||||
s->rx_ctl = value;
|
||||
break;
|
||||
case EMAC_RX_FBC_REG:
|
||||
if (value == 0) {
|
||||
aw_emac_rx_reset(s);
|
||||
}
|
||||
break;
|
||||
case EMAC_INT_CTL_REG:
|
||||
s->int_ctl = value;
|
||||
break;
|
||||
case EMAC_INT_STA_REG:
|
||||
s->int_sta &= ~value;
|
||||
break;
|
||||
case EMAC_MAC_MADR_REG:
|
||||
s->phy_target = value;
|
||||
break;
|
||||
case EMAC_MAC_MWTD_REG:
|
||||
RTL8201CP_mdio_write(s, extract32(s->phy_target, PHY_ADDR_SHIFT, 8),
|
||||
extract32(s->phy_target, PHY_REG_SHIFT, 8), value);
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"allwinner_emac: write access to unknown register 0x"
|
||||
TARGET_FMT_plx "\n", offset);
|
||||
}
|
||||
}
|
||||
|
||||
static void aw_emac_set_link(NetClientState *nc)
|
||||
{
|
||||
AwEmacState *s = qemu_get_nic_opaque(nc);
|
||||
|
||||
mii_set_link(&s->mii, !nc->link_down);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps aw_emac_mem_ops = {
|
||||
.read = aw_emac_read,
|
||||
.write = aw_emac_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static NetClientInfo net_aw_emac_info = {
|
||||
.type = NET_CLIENT_OPTIONS_KIND_NIC,
|
||||
.size = sizeof(NICState),
|
||||
.can_receive = aw_emac_can_receive,
|
||||
.receive = aw_emac_receive,
|
||||
.cleanup = aw_emac_cleanup,
|
||||
.link_status_changed = aw_emac_set_link,
|
||||
};
|
||||
|
||||
static void aw_emac_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
AwEmacState *s = AW_EMAC(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &aw_emac_mem_ops, s,
|
||||
"aw_emac", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
}
|
||||
|
||||
static void aw_emac_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
AwEmacState *s = AW_EMAC(dev);
|
||||
|
||||
qemu_macaddr_default_if_unset(&s->conf.macaddr);
|
||||
s->nic = qemu_new_nic(&net_aw_emac_info, &s->conf,
|
||||
object_get_typename(OBJECT(dev)), dev->id, s);
|
||||
qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
|
||||
|
||||
fifo8_create(&s->rx_fifo, RX_FIFO_SIZE);
|
||||
fifo8_create(&s->tx_fifo[0], TX_FIFO_SIZE);
|
||||
fifo8_create(&s->tx_fifo[1], TX_FIFO_SIZE);
|
||||
}
|
||||
|
||||
static Property aw_emac_properties[] = {
|
||||
DEFINE_NIC_PROPERTIES(AwEmacState, conf),
|
||||
DEFINE_PROP_UINT8("phy-addr", AwEmacState, phy_addr, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_mii = {
|
||||
.name = "rtl8201cp",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT16(bmcr, RTL8201CPState),
|
||||
VMSTATE_UINT16(bmsr, RTL8201CPState),
|
||||
VMSTATE_UINT16(anar, RTL8201CPState),
|
||||
VMSTATE_UINT16(anlpar, RTL8201CPState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static int aw_emac_post_load(void *opaque, int version_id)
|
||||
{
|
||||
AwEmacState *s = opaque;
|
||||
|
||||
aw_emac_set_link(qemu_get_queue(s->nic));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_aw_emac = {
|
||||
.name = "allwinner_emac",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.post_load = aw_emac_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_STRUCT(mii, AwEmacState, 1, vmstate_mii, RTL8201CPState),
|
||||
VMSTATE_UINT32(ctl, AwEmacState),
|
||||
VMSTATE_UINT32(tx_mode, AwEmacState),
|
||||
VMSTATE_UINT32(rx_ctl, AwEmacState),
|
||||
VMSTATE_UINT32(int_ctl, AwEmacState),
|
||||
VMSTATE_UINT32(int_sta, AwEmacState),
|
||||
VMSTATE_UINT32(phy_target, AwEmacState),
|
||||
VMSTATE_FIFO8(rx_fifo, AwEmacState),
|
||||
VMSTATE_UINT32(rx_num_packets, AwEmacState),
|
||||
VMSTATE_UINT32(rx_packet_size, AwEmacState),
|
||||
VMSTATE_UINT32(rx_packet_pos, AwEmacState),
|
||||
VMSTATE_STRUCT_ARRAY(tx_fifo, AwEmacState, NUM_TX_FIFOS, 1,
|
||||
vmstate_fifo8, Fifo8),
|
||||
VMSTATE_UINT32_ARRAY(tx_length, AwEmacState, NUM_TX_FIFOS),
|
||||
VMSTATE_UINT32(tx_channel, AwEmacState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void aw_emac_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = aw_emac_realize;
|
||||
dc->props = aw_emac_properties;
|
||||
dc->reset = aw_emac_reset;
|
||||
dc->vmsd = &vmstate_aw_emac;
|
||||
}
|
||||
|
||||
static const TypeInfo aw_emac_info = {
|
||||
.name = TYPE_AW_EMAC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(AwEmacState),
|
||||
.instance_init = aw_emac_init,
|
||||
.class_init = aw_emac_class_init,
|
||||
};
|
||||
|
||||
static void aw_emac_register_types(void)
|
||||
{
|
||||
type_register_static(&aw_emac_info);
|
||||
}
|
||||
|
||||
type_init(aw_emac_register_types)
|
|
@ -379,6 +379,7 @@ int print_insn_h8300 (bfd_vma, disassemble_info*);
|
|||
int print_insn_h8300h (bfd_vma, disassemble_info*);
|
||||
int print_insn_h8300s (bfd_vma, disassemble_info*);
|
||||
int print_insn_h8500 (bfd_vma, disassemble_info*);
|
||||
int print_insn_arm_a64 (bfd_vma, disassemble_info*);
|
||||
int print_insn_alpha (bfd_vma, disassemble_info*);
|
||||
disassembler_ftype arc_get_disassembler (int, int);
|
||||
int print_insn_arm (bfd_vma, disassemble_info*);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "hw/arm/arm.h"
|
||||
#include "hw/timer/allwinner-a10-pit.h"
|
||||
#include "hw/intc/allwinner-a10-pic.h"
|
||||
#include "hw/net/allwinner_emac.h"
|
||||
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "exec/address-spaces.h"
|
||||
|
@ -14,6 +15,7 @@
|
|||
#define AW_A10_PIC_REG_BASE 0x01c20400
|
||||
#define AW_A10_PIT_REG_BASE 0x01c20c00
|
||||
#define AW_A10_UART0_REG_BASE 0x01c28000
|
||||
#define AW_A10_EMAC_BASE 0x01c0b000
|
||||
|
||||
#define AW_A10_SDRAM_BASE 0x40000000
|
||||
|
||||
|
@ -29,6 +31,7 @@ typedef struct AwA10State {
|
|||
qemu_irq irq[AW_A10_PIC_INT_NR];
|
||||
AwA10PITState timer;
|
||||
AwA10PICState intc;
|
||||
AwEmacState emac;
|
||||
} AwA10State;
|
||||
|
||||
#define ALLWINNER_H_
|
||||
|
|
|
@ -31,6 +31,9 @@
|
|||
/* Maximum number of possible CPU interfaces, determined by GIC architecture */
|
||||
#define GIC_NCPU 8
|
||||
|
||||
#define MAX_NR_GROUP_PRIO 128
|
||||
#define GIC_NR_APRS (MAX_NR_GROUP_PRIO / 32)
|
||||
|
||||
typedef struct gic_irq_state {
|
||||
/* The enable bits are only banked for per-cpu interrupts. */
|
||||
uint8_t enabled;
|
||||
|
@ -55,12 +58,42 @@ typedef struct GICState {
|
|||
uint8_t priority1[GIC_INTERNAL][GIC_NCPU];
|
||||
uint8_t priority2[GIC_MAXIRQ - GIC_INTERNAL];
|
||||
uint16_t last_active[GIC_MAXIRQ][GIC_NCPU];
|
||||
/* For each SGI on the target CPU, we store 8 bits
|
||||
* indicating which source CPUs have made this SGI
|
||||
* pending on the target CPU. These correspond to
|
||||
* the bytes in the GIC_SPENDSGIR* registers as
|
||||
* read by the target CPU.
|
||||
*/
|
||||
uint8_t sgi_pending[GIC_NR_SGIS][GIC_NCPU];
|
||||
|
||||
uint16_t priority_mask[GIC_NCPU];
|
||||
uint16_t running_irq[GIC_NCPU];
|
||||
uint16_t running_priority[GIC_NCPU];
|
||||
uint16_t current_pending[GIC_NCPU];
|
||||
|
||||
/* We present the GICv2 without security extensions to a guest and
|
||||
* therefore the guest can configure the GICC_CTLR to configure group 1
|
||||
* binary point in the abpr.
|
||||
*/
|
||||
uint8_t bpr[GIC_NCPU];
|
||||
uint8_t abpr[GIC_NCPU];
|
||||
|
||||
/* The APR is implementation defined, so we choose a layout identical to
|
||||
* the KVM ABI layout for QEMU's implementation of the gic:
|
||||
* If an interrupt for preemption level X is active, then
|
||||
* APRn[X mod 32] == 0b1, where n = X / 32
|
||||
* otherwise the bit is clear.
|
||||
*
|
||||
* TODO: rewrite the interrupt acknowlege/complete routines to use
|
||||
* the APR registers to track the necessary information to update
|
||||
* s->running_priority[] on interrupt completion (ie completely remove
|
||||
* last_active[][] and running_irq[]). This will be necessary if we ever
|
||||
* want to support TCG<->KVM migration, or TCG guests which can
|
||||
* do power management involving powering down and restarting
|
||||
* the GIC.
|
||||
*/
|
||||
uint32_t apr[GIC_NR_APRS][GIC_NCPU];
|
||||
|
||||
uint32_t num_cpu;
|
||||
|
||||
MemoryRegion iomem; /* Distributor */
|
||||
|
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* Emulation of Allwinner EMAC Fast Ethernet controller and
|
||||
* Realtek RTL8201CP PHY
|
||||
*
|
||||
* Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
|
||||
*
|
||||
* Allwinner EMAC register definitions from Linux kernel are:
|
||||
* Copyright 2012 Stefan Roese <sr@denx.de>
|
||||
* Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
|
||||
* Copyright 1997 Sten Wang
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
#ifndef AW_EMAC_H
|
||||
#define AW_EMAC_H
|
||||
|
||||
#include "net/net.h"
|
||||
#include "qemu/fifo8.h"
|
||||
|
||||
#define TYPE_AW_EMAC "allwinner-emac"
|
||||
#define AW_EMAC(obj) OBJECT_CHECK(AwEmacState, (obj), TYPE_AW_EMAC)
|
||||
|
||||
/*
|
||||
* Allwinner EMAC register list
|
||||
*/
|
||||
#define EMAC_CTL_REG 0x00
|
||||
|
||||
#define EMAC_TX_MODE_REG 0x04
|
||||
#define EMAC_TX_FLOW_REG 0x08
|
||||
#define EMAC_TX_CTL0_REG 0x0C
|
||||
#define EMAC_TX_CTL1_REG 0x10
|
||||
#define EMAC_TX_INS_REG 0x14
|
||||
#define EMAC_TX_PL0_REG 0x18
|
||||
#define EMAC_TX_PL1_REG 0x1C
|
||||
#define EMAC_TX_STA_REG 0x20
|
||||
#define EMAC_TX_IO_DATA_REG 0x24
|
||||
#define EMAC_TX_IO_DATA1_REG 0x28
|
||||
#define EMAC_TX_TSVL0_REG 0x2C
|
||||
#define EMAC_TX_TSVH0_REG 0x30
|
||||
#define EMAC_TX_TSVL1_REG 0x34
|
||||
#define EMAC_TX_TSVH1_REG 0x38
|
||||
|
||||
#define EMAC_RX_CTL_REG 0x3C
|
||||
#define EMAC_RX_HASH0_REG 0x40
|
||||
#define EMAC_RX_HASH1_REG 0x44
|
||||
#define EMAC_RX_STA_REG 0x48
|
||||
#define EMAC_RX_IO_DATA_REG 0x4C
|
||||
#define EMAC_RX_FBC_REG 0x50
|
||||
|
||||
#define EMAC_INT_CTL_REG 0x54
|
||||
#define EMAC_INT_STA_REG 0x58
|
||||
|
||||
#define EMAC_MAC_CTL0_REG 0x5C
|
||||
#define EMAC_MAC_CTL1_REG 0x60
|
||||
#define EMAC_MAC_IPGT_REG 0x64
|
||||
#define EMAC_MAC_IPGR_REG 0x68
|
||||
#define EMAC_MAC_CLRT_REG 0x6C
|
||||
#define EMAC_MAC_MAXF_REG 0x70
|
||||
#define EMAC_MAC_SUPP_REG 0x74
|
||||
#define EMAC_MAC_TEST_REG 0x78
|
||||
#define EMAC_MAC_MCFG_REG 0x7C
|
||||
#define EMAC_MAC_MCMD_REG 0x80
|
||||
#define EMAC_MAC_MADR_REG 0x84
|
||||
#define EMAC_MAC_MWTD_REG 0x88
|
||||
#define EMAC_MAC_MRDD_REG 0x8C
|
||||
#define EMAC_MAC_MIND_REG 0x90
|
||||
#define EMAC_MAC_SSRR_REG 0x94
|
||||
#define EMAC_MAC_A0_REG 0x98
|
||||
#define EMAC_MAC_A1_REG 0x9C
|
||||
#define EMAC_MAC_A2_REG 0xA0
|
||||
|
||||
#define EMAC_SAFX_L_REG0 0xA4
|
||||
#define EMAC_SAFX_H_REG0 0xA8
|
||||
#define EMAC_SAFX_L_REG1 0xAC
|
||||
#define EMAC_SAFX_H_REG1 0xB0
|
||||
#define EMAC_SAFX_L_REG2 0xB4
|
||||
#define EMAC_SAFX_H_REG2 0xB8
|
||||
#define EMAC_SAFX_L_REG3 0xBC
|
||||
#define EMAC_SAFX_H_REG3 0xC0
|
||||
|
||||
/* CTL register fields */
|
||||
#define EMAC_CTL_RESET (1 << 0)
|
||||
#define EMAC_CTL_TX_EN (1 << 1)
|
||||
#define EMAC_CTL_RX_EN (1 << 2)
|
||||
|
||||
/* TX MODE register fields */
|
||||
#define EMAC_TX_MODE_ABORTED_FRAME_EN (1 << 0)
|
||||
#define EMAC_TX_MODE_DMA_EN (1 << 1)
|
||||
|
||||
/* RX CTL register fields */
|
||||
#define EMAC_RX_CTL_AUTO_DRQ_EN (1 << 1)
|
||||
#define EMAC_RX_CTL_DMA_EN (1 << 2)
|
||||
#define EMAC_RX_CTL_PASS_ALL_EN (1 << 4)
|
||||
#define EMAC_RX_CTL_PASS_CTL_EN (1 << 5)
|
||||
#define EMAC_RX_CTL_PASS_CRC_ERR_EN (1 << 6)
|
||||
#define EMAC_RX_CTL_PASS_LEN_ERR_EN (1 << 7)
|
||||
#define EMAC_RX_CTL_PASS_LEN_OOR_EN (1 << 8)
|
||||
#define EMAC_RX_CTL_ACCEPT_UNICAST_EN (1 << 16)
|
||||
#define EMAC_RX_CTL_DA_FILTER_EN (1 << 17)
|
||||
#define EMAC_RX_CTL_ACCEPT_MULTICAST_EN (1 << 20)
|
||||
#define EMAC_RX_CTL_HASH_FILTER_EN (1 << 21)
|
||||
#define EMAC_RX_CTL_ACCEPT_BROADCAST_EN (1 << 22)
|
||||
#define EMAC_RX_CTL_SA_FILTER_EN (1 << 24)
|
||||
#define EMAC_RX_CTL_SA_FILTER_INVERT_EN (1 << 25)
|
||||
|
||||
/* RX IO DATA register fields */
|
||||
#define EMAC_RX_HEADER(len, status) (((len) & 0xffff) | ((status) << 16))
|
||||
#define EMAC_RX_IO_DATA_STATUS_CRC_ERR (1 << 4)
|
||||
#define EMAC_RX_IO_DATA_STATUS_LEN_ERR (3 << 5)
|
||||
#define EMAC_RX_IO_DATA_STATUS_OK (1 << 7)
|
||||
#define EMAC_UNDOCUMENTED_MAGIC 0x0143414d /* header for RX frames */
|
||||
|
||||
/* PHY registers */
|
||||
#define MII_BMCR 0
|
||||
#define MII_BMSR 1
|
||||
#define MII_PHYID1 2
|
||||
#define MII_PHYID2 3
|
||||
#define MII_ANAR 4
|
||||
#define MII_ANLPAR 5
|
||||
#define MII_ANER 6
|
||||
#define MII_NSR 16
|
||||
#define MII_LBREMR 17
|
||||
#define MII_REC 18
|
||||
#define MII_SNRDR 19
|
||||
#define MII_TEST 25
|
||||
|
||||
/* PHY registers fields */
|
||||
#define MII_BMCR_RESET (1 << 15)
|
||||
#define MII_BMCR_LOOPBACK (1 << 14)
|
||||
#define MII_BMCR_SPEED (1 << 13)
|
||||
#define MII_BMCR_AUTOEN (1 << 12)
|
||||
#define MII_BMCR_FD (1 << 8)
|
||||
|
||||
#define MII_BMSR_100TX_FD (1 << 14)
|
||||
#define MII_BMSR_100TX_HD (1 << 13)
|
||||
#define MII_BMSR_10T_FD (1 << 12)
|
||||
#define MII_BMSR_10T_HD (1 << 11)
|
||||
#define MII_BMSR_MFPS (1 << 6)
|
||||
#define MII_BMSR_AUTONEG (1 << 3)
|
||||
#define MII_BMSR_LINK_ST (1 << 2)
|
||||
|
||||
#define MII_ANAR_TXFD (1 << 8)
|
||||
#define MII_ANAR_TX (1 << 7)
|
||||
#define MII_ANAR_10FD (1 << 6)
|
||||
#define MII_ANAR_10 (1 << 5)
|
||||
#define MII_ANAR_CSMACD (1 << 0)
|
||||
|
||||
#define RTL8201CP_PHYID1 0x0000
|
||||
#define RTL8201CP_PHYID2 0x8201
|
||||
|
||||
/* INT CTL and INT STA registers fields */
|
||||
#define EMAC_INT_TX_CHAN(x) (1 << (x))
|
||||
#define EMAC_INT_RX (1 << 8)
|
||||
|
||||
/* Due to lack of specifications, size of fifos is chosen arbitrarily */
|
||||
#define TX_FIFO_SIZE (4 * 1024)
|
||||
#define RX_FIFO_SIZE (32 * 1024)
|
||||
|
||||
#define NUM_TX_FIFOS 2
|
||||
#define RX_HDR_SIZE 8
|
||||
#define CRC_SIZE 4
|
||||
|
||||
#define PHY_REG_SHIFT 0
|
||||
#define PHY_ADDR_SHIFT 8
|
||||
|
||||
typedef struct RTL8201CPState {
|
||||
uint16_t bmcr;
|
||||
uint16_t bmsr;
|
||||
uint16_t anar;
|
||||
uint16_t anlpar;
|
||||
} RTL8201CPState;
|
||||
|
||||
typedef struct AwEmacState {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
NICState *nic;
|
||||
NICConf conf;
|
||||
RTL8201CPState mii;
|
||||
uint8_t phy_addr;
|
||||
|
||||
uint32_t ctl;
|
||||
uint32_t tx_mode;
|
||||
uint32_t rx_ctl;
|
||||
uint32_t int_ctl;
|
||||
uint32_t int_sta;
|
||||
uint32_t phy_target;
|
||||
|
||||
Fifo8 rx_fifo;
|
||||
uint32_t rx_num_packets;
|
||||
uint32_t rx_packet_size;
|
||||
uint32_t rx_packet_pos;
|
||||
|
||||
Fifo8 tx_fifo[NUM_TX_FIFOS];
|
||||
uint32_t tx_length[NUM_TX_FIFOS];
|
||||
uint32_t tx_channel;
|
||||
} AwEmacState;
|
||||
|
||||
#endif
|
|
@ -656,9 +656,15 @@ extern const VMStateInfo vmstate_info_bitmap;
|
|||
#define VMSTATE_UINT32_ARRAY_V(_f, _s, _n, _v) \
|
||||
VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint32, uint32_t)
|
||||
|
||||
#define VMSTATE_UINT32_2DARRAY_V(_f, _s, _n1, _n2, _v) \
|
||||
VMSTATE_2DARRAY(_f, _s, _n1, _n2, _v, vmstate_info_uint32, uint32_t)
|
||||
|
||||
#define VMSTATE_UINT32_ARRAY(_f, _s, _n) \
|
||||
VMSTATE_UINT32_ARRAY_V(_f, _s, _n, 0)
|
||||
|
||||
#define VMSTATE_UINT32_2DARRAY(_f, _s, _n1, _n2) \
|
||||
VMSTATE_UINT32_2DARRAY_V(_f, _s, _n1, _n2, 0)
|
||||
|
||||
#define VMSTATE_UINT64_ARRAY_V(_f, _s, _n, _v) \
|
||||
VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint64, uint64_t)
|
||||
|
||||
|
|
|
@ -43,6 +43,19 @@ void fifo8_destroy(Fifo8 *fifo);
|
|||
|
||||
void fifo8_push(Fifo8 *fifo, uint8_t data);
|
||||
|
||||
/**
|
||||
* fifo8_push_all:
|
||||
* @fifo: FIFO to push to
|
||||
* @data: data to push
|
||||
* @size: number of bytes to push
|
||||
*
|
||||
* Push a byte array to the FIFO. Behaviour is undefined if the FIFO is full.
|
||||
* Clients are responsible for checking the space left in the FIFO using
|
||||
* fifo8_num_free().
|
||||
*/
|
||||
|
||||
void fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num);
|
||||
|
||||
/**
|
||||
* fifo8_pop:
|
||||
* @fifo: fifo to pop from
|
||||
|
@ -55,6 +68,32 @@ void fifo8_push(Fifo8 *fifo, uint8_t data);
|
|||
|
||||
uint8_t fifo8_pop(Fifo8 *fifo);
|
||||
|
||||
/**
|
||||
* fifo8_pop_buf:
|
||||
* @fifo: FIFO to pop from
|
||||
* @max: maximum number of bytes to pop
|
||||
* @num: actual number of returned bytes
|
||||
*
|
||||
* Pop a number of elements from the FIFO up to a maximum of max. The buffer
|
||||
* containing the popped data is returned. This buffer points directly into
|
||||
* the FIFO backing store and data is invalidated once any of the fifo8_* APIs
|
||||
* are called on the FIFO.
|
||||
*
|
||||
* The function may return fewer bytes than requested when the data wraps
|
||||
* around in the ring buffer; in this case only a contiguous part of the data
|
||||
* is returned.
|
||||
*
|
||||
* The number of valid bytes returned is populated in *num; will always return
|
||||
* at least 1 byte. max must not be 0 or greater than the number of bytes in
|
||||
* the FIFO.
|
||||
*
|
||||
* Clients are responsible for checking the availability of requested data
|
||||
* using fifo8_num_used().
|
||||
*
|
||||
* Returns: A pointer to popped data.
|
||||
*/
|
||||
const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *num);
|
||||
|
||||
/**
|
||||
* fifo8_reset:
|
||||
* @fifo: FIFO to reset
|
||||
|
@ -86,6 +125,28 @@ bool fifo8_is_empty(Fifo8 *fifo);
|
|||
|
||||
bool fifo8_is_full(Fifo8 *fifo);
|
||||
|
||||
/**
|
||||
* fifo8_num_free:
|
||||
* @fifo: FIFO to check
|
||||
*
|
||||
* Return the number of free bytes in the FIFO.
|
||||
*
|
||||
* Returns: Number of free bytes.
|
||||
*/
|
||||
|
||||
uint32_t fifo8_num_free(Fifo8 *fifo);
|
||||
|
||||
/**
|
||||
* fifo8_num_used:
|
||||
* @fifo: FIFO to check
|
||||
*
|
||||
* Return the number of used bytes in the FIFO.
|
||||
*
|
||||
* Returns: Number of used bytes.
|
||||
*/
|
||||
|
||||
uint32_t fifo8_num_used(Fifo8 *fifo);
|
||||
|
||||
extern const VMStateDescription vmstate_fifo8;
|
||||
|
||||
#define VMSTATE_FIFO8(_field, _state) { \
|
||||
|
|
14
rules.mak
14
rules.mak
|
@ -8,6 +8,7 @@ MAKEFLAGS += -rR
|
|||
%.d:
|
||||
%.h:
|
||||
%.c:
|
||||
%.cc:
|
||||
%.cpp:
|
||||
%.m:
|
||||
%.mak:
|
||||
|
@ -26,8 +27,12 @@ QEMU_INCLUDES += -I$(<D) -I$(@D)
|
|||
%.o: %.rc
|
||||
$(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@")
|
||||
|
||||
# If we have a CXX we might have some C++ objects, in which case we
|
||||
# must link with the C++ compiler, not the plain C compiler.
|
||||
LINKPROG = $(or $(CXX),$(CC))
|
||||
|
||||
ifeq ($(LIBTOOL),)
|
||||
LINK = $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \
|
||||
LINK = $(call quiet-command,$(LINKPROG) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \
|
||||
$(sort $(filter %.o, $1)) $(filter-out %.o, $1) $(version-obj-y) \
|
||||
$(LIBS)," LINK $(TARGET_DIR)$@")
|
||||
else
|
||||
|
@ -41,7 +46,7 @@ LIBTOOL += $(if $(V),,--quiet)
|
|||
|
||||
LINK = $(call quiet-command,\
|
||||
$(if $(filter %.lo %.la,$^),$(LIBTOOL) --mode=link --tag=CC \
|
||||
)$(CC) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \
|
||||
)$(LINKPROG) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \
|
||||
$(sort $(filter %.o, $1)) $(filter-out %.o, $1) \
|
||||
$(if $(filter %.lo %.la,$^),$(version-lobj-y),$(version-obj-y)) \
|
||||
$(if $(filter %.lo %.la,$^),$(LIBTOOLFLAGS)) \
|
||||
|
@ -54,6 +59,9 @@ endif
|
|||
%.o: %.asm
|
||||
$(call quiet-command,$(AS) $(ASFLAGS) -o $@ $<," AS $(TARGET_DIR)$@")
|
||||
|
||||
%.o: %.cc
|
||||
$(call quiet-command,$(CXX) $(QEMU_INCLUDES) $(QEMU_CXXFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," CXX $(TARGET_DIR)$@")
|
||||
|
||||
%.o: %.cpp
|
||||
$(call quiet-command,$(CXX) $(QEMU_INCLUDES) $(QEMU_CXXFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," CXX $(TARGET_DIR)$@")
|
||||
|
||||
|
@ -77,7 +85,7 @@ quiet-command = $(if $(V),$1,$(if $(2),@echo $2 && $1, @$1))
|
|||
cc-option = $(if $(shell $(CC) $1 $2 -S -o /dev/null -xc /dev/null \
|
||||
>/dev/null 2>&1 && echo OK), $2, $3)
|
||||
|
||||
VPATH_SUFFIXES = %.c %.h %.S %.cpp %.m %.mak %.texi %.sh %.rc
|
||||
VPATH_SUFFIXES = %.c %.h %.S %.cc %.cpp %.m %.mak %.texi %.sh %.rc
|
||||
set-vpath = $(if $1,$(foreach PATTERN,$(VPATH_SUFFIXES),$(eval vpath $(PATTERN) $1)))
|
||||
|
||||
# find-in-path
|
||||
|
|
|
@ -320,6 +320,7 @@ DEF_HELPER_1(neon_cls_s8, i32, i32)
|
|||
DEF_HELPER_1(neon_cls_s16, i32, i32)
|
||||
DEF_HELPER_1(neon_cls_s32, i32, i32)
|
||||
DEF_HELPER_1(neon_cnt_u8, i32, i32)
|
||||
DEF_HELPER_FLAGS_1(neon_rbit_u8, TCG_CALL_NO_RWG_SE, i32, i32)
|
||||
|
||||
DEF_HELPER_3(neon_qdmulh_s16, i32, env, i32, i32)
|
||||
DEF_HELPER_3(neon_qrdmulh_s16, i32, env, i32, i32)
|
||||
|
|
|
@ -1133,6 +1133,18 @@ uint32_t HELPER(neon_cnt_u8)(uint32_t x)
|
|||
return x;
|
||||
}
|
||||
|
||||
/* Reverse bits in each 8 bit word */
|
||||
uint32_t HELPER(neon_rbit_u8)(uint32_t x)
|
||||
{
|
||||
x = ((x & 0xf0f0f0f0) >> 4)
|
||||
| ((x & 0x0f0f0f0f) << 4);
|
||||
x = ((x & 0x88888888) >> 3)
|
||||
| ((x & 0x44444444) >> 1)
|
||||
| ((x & 0x22222222) << 1)
|
||||
| ((x & 0x11111111) << 3);
|
||||
return x;
|
||||
}
|
||||
|
||||
#define NEON_QDMULH16(dest, src1, src2, round) do { \
|
||||
uint32_t tmp = (int32_t)(int16_t) src1 * (int16_t) src2; \
|
||||
if ((tmp ^ (tmp << 1)) & SIGNBIT) { \
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3142,16 +3142,19 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
|
|||
VFP_DREG_N(rn, insn);
|
||||
}
|
||||
|
||||
if (op == 15 && (rn == 15 || ((rn & 0x1c) == 0x18))) {
|
||||
/* Integer or single precision destination. */
|
||||
if (op == 15 && (rn == 15 || ((rn & 0x1c) == 0x18) ||
|
||||
((rn & 0x1e) == 0x6))) {
|
||||
/* Integer or single/half precision destination. */
|
||||
rd = VFP_SREG_D(insn);
|
||||
} else {
|
||||
VFP_DREG_D(rd, insn);
|
||||
}
|
||||
if (op == 15 &&
|
||||
(((rn & 0x1c) == 0x10) || ((rn & 0x14) == 0x14))) {
|
||||
/* VCVT from int is always from S reg regardless of dp bit.
|
||||
* VCVT with immediate frac_bits has same format as SREG_M
|
||||
(((rn & 0x1c) == 0x10) || ((rn & 0x14) == 0x14) ||
|
||||
((rn & 0x1e) == 0x4))) {
|
||||
/* VCVT from int or half precision is always from S reg
|
||||
* regardless of dp bit. VCVT with immediate frac_bits
|
||||
* has same format as SREG_M.
|
||||
*/
|
||||
rm = VFP_SREG_M(insn);
|
||||
} else {
|
||||
|
@ -3241,12 +3244,19 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
|
|||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
/* VCVTB, VCVTT: only present with the halfprec extension,
|
||||
* UNPREDICTABLE if bit 8 is set (we choose to UNDEF)
|
||||
/* VCVTB, VCVTT: only present with the halfprec extension
|
||||
* UNPREDICTABLE if bit 8 is set prior to ARMv8
|
||||
* (we choose to UNDEF)
|
||||
*/
|
||||
if (dp || !arm_feature(env, ARM_FEATURE_VFP_FP16)) {
|
||||
if ((dp && !arm_feature(env, ARM_FEATURE_V8)) ||
|
||||
!arm_feature(env, ARM_FEATURE_VFP_FP16)) {
|
||||
return 1;
|
||||
}
|
||||
if (!extract32(rn, 1, 1)) {
|
||||
/* Half precision source. */
|
||||
gen_mov_F0_vreg(0, rm);
|
||||
break;
|
||||
}
|
||||
/* Otherwise fall through */
|
||||
default:
|
||||
/* One source operand. */
|
||||
|
@ -3394,21 +3404,39 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
|
|||
case 3: /* sqrt */
|
||||
gen_vfp_sqrt(dp);
|
||||
break;
|
||||
case 4: /* vcvtb.f32.f16 */
|
||||
case 4: /* vcvtb.f32.f16, vcvtb.f64.f16 */
|
||||
tmp = gen_vfp_mrs();
|
||||
tcg_gen_ext16u_i32(tmp, tmp);
|
||||
gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp, cpu_env);
|
||||
if (dp) {
|
||||
gen_helper_vfp_fcvt_f16_to_f64(cpu_F0d, tmp,
|
||||
cpu_env);
|
||||
} else {
|
||||
gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp,
|
||||
cpu_env);
|
||||
}
|
||||
tcg_temp_free_i32(tmp);
|
||||
break;
|
||||
case 5: /* vcvtt.f32.f16 */
|
||||
case 5: /* vcvtt.f32.f16, vcvtt.f64.f16 */
|
||||
tmp = gen_vfp_mrs();
|
||||
tcg_gen_shri_i32(tmp, tmp, 16);
|
||||
gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp, cpu_env);
|
||||
if (dp) {
|
||||
gen_helper_vfp_fcvt_f16_to_f64(cpu_F0d, tmp,
|
||||
cpu_env);
|
||||
} else {
|
||||
gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp,
|
||||
cpu_env);
|
||||
}
|
||||
tcg_temp_free_i32(tmp);
|
||||
break;
|
||||
case 6: /* vcvtb.f16.f32 */
|
||||
case 6: /* vcvtb.f16.f32, vcvtb.f16.f64 */
|
||||
tmp = tcg_temp_new_i32();
|
||||
gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s, cpu_env);
|
||||
if (dp) {
|
||||
gen_helper_vfp_fcvt_f64_to_f16(tmp, cpu_F0d,
|
||||
cpu_env);
|
||||
} else {
|
||||
gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s,
|
||||
cpu_env);
|
||||
}
|
||||
gen_mov_F0_vreg(0, rd);
|
||||
tmp2 = gen_vfp_mrs();
|
||||
tcg_gen_andi_i32(tmp2, tmp2, 0xffff0000);
|
||||
|
@ -3416,9 +3444,15 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
|
|||
tcg_temp_free_i32(tmp2);
|
||||
gen_vfp_msr(tmp);
|
||||
break;
|
||||
case 7: /* vcvtt.f16.f32 */
|
||||
case 7: /* vcvtt.f16.f32, vcvtt.f16.f64 */
|
||||
tmp = tcg_temp_new_i32();
|
||||
gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s, cpu_env);
|
||||
if (dp) {
|
||||
gen_helper_vfp_fcvt_f64_to_f16(tmp, cpu_F0d,
|
||||
cpu_env);
|
||||
} else {
|
||||
gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s,
|
||||
cpu_env);
|
||||
}
|
||||
tcg_gen_shli_i32(tmp, tmp, 16);
|
||||
gen_mov_F0_vreg(0, rd);
|
||||
tmp2 = gen_vfp_mrs();
|
||||
|
@ -3551,16 +3585,21 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
|
|||
}
|
||||
|
||||
/* Write back the result. */
|
||||
if (op == 15 && (rn >= 8 && rn <= 11))
|
||||
; /* Comparison, do nothing. */
|
||||
else if (op == 15 && dp && ((rn & 0x1c) == 0x18))
|
||||
/* VCVT double to int: always integer result. */
|
||||
if (op == 15 && (rn >= 8 && rn <= 11)) {
|
||||
/* Comparison, do nothing. */
|
||||
} else if (op == 15 && dp && ((rn & 0x1c) == 0x18 ||
|
||||
(rn & 0x1e) == 0x6)) {
|
||||
/* VCVT double to int: always integer result.
|
||||
* VCVT double to half precision is always a single
|
||||
* precision result.
|
||||
*/
|
||||
gen_mov_vreg_F0(0, rd);
|
||||
else if (op == 15 && rn == 15)
|
||||
} else if (op == 15 && rn == 15) {
|
||||
/* conversion */
|
||||
gen_mov_vreg_F0(!dp, rd);
|
||||
else
|
||||
} else {
|
||||
gen_mov_vreg_F0(dp, rd);
|
||||
}
|
||||
|
||||
/* break out of the loop if we have finished */
|
||||
if (veclen == 0)
|
||||
|
|
|
@ -324,13 +324,16 @@ typedef int TCGv_i64;
|
|||
|
||||
#define TCGV_EQUAL_I32(a, b) (GET_TCGV_I32(a) == GET_TCGV_I32(b))
|
||||
#define TCGV_EQUAL_I64(a, b) (GET_TCGV_I64(a) == GET_TCGV_I64(b))
|
||||
#define TCGV_EQUAL_PTR(a, b) (GET_TCGV_PTR(a) == GET_TCGV_PTR(b))
|
||||
|
||||
/* Dummy definition to avoid compiler warnings. */
|
||||
#define TCGV_UNUSED_I32(x) x = MAKE_TCGV_I32(-1)
|
||||
#define TCGV_UNUSED_I64(x) x = MAKE_TCGV_I64(-1)
|
||||
#define TCGV_UNUSED_PTR(x) x = MAKE_TCGV_PTR(-1)
|
||||
|
||||
#define TCGV_IS_UNUSED_I32(x) (GET_TCGV_I32(x) == -1)
|
||||
#define TCGV_IS_UNUSED_I64(x) (GET_TCGV_I64(x) == -1)
|
||||
#define TCGV_IS_UNUSED_PTR(x) (GET_TCGV_PTR(x) == -1)
|
||||
|
||||
/* call flags */
|
||||
/* Helper does not read globals (either directly or through an exception). It
|
||||
|
|
47
util/fifo8.c
47
util/fifo8.c
|
@ -37,6 +37,27 @@ void fifo8_push(Fifo8 *fifo, uint8_t data)
|
|||
fifo->num++;
|
||||
}
|
||||
|
||||
void fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num)
|
||||
{
|
||||
uint32_t start, avail;
|
||||
|
||||
if (fifo->num + num > fifo->capacity) {
|
||||
abort();
|
||||
}
|
||||
|
||||
start = (fifo->head + fifo->num) % fifo->capacity;
|
||||
|
||||
if (start + num <= fifo->capacity) {
|
||||
memcpy(&fifo->data[start], data, num);
|
||||
} else {
|
||||
avail = fifo->capacity - start;
|
||||
memcpy(&fifo->data[start], data, avail);
|
||||
memcpy(&fifo->data[0], &data[avail], num - avail);
|
||||
}
|
||||
|
||||
fifo->num += num;
|
||||
}
|
||||
|
||||
uint8_t fifo8_pop(Fifo8 *fifo)
|
||||
{
|
||||
uint8_t ret;
|
||||
|
@ -50,9 +71,25 @@ uint8_t fifo8_pop(Fifo8 *fifo)
|
|||
return ret;
|
||||
}
|
||||
|
||||
const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *num)
|
||||
{
|
||||
uint8_t *ret;
|
||||
|
||||
if (max == 0 || max > fifo->num) {
|
||||
abort();
|
||||
}
|
||||
*num = MIN(fifo->capacity - fifo->head, max);
|
||||
ret = &fifo->data[fifo->head];
|
||||
fifo->head += *num;
|
||||
fifo->head %= fifo->capacity;
|
||||
fifo->num -= *num;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void fifo8_reset(Fifo8 *fifo)
|
||||
{
|
||||
fifo->num = 0;
|
||||
fifo->head = 0;
|
||||
}
|
||||
|
||||
bool fifo8_is_empty(Fifo8 *fifo)
|
||||
|
@ -65,6 +102,16 @@ bool fifo8_is_full(Fifo8 *fifo)
|
|||
return (fifo->num == fifo->capacity);
|
||||
}
|
||||
|
||||
uint32_t fifo8_num_free(Fifo8 *fifo)
|
||||
{
|
||||
return fifo->capacity - fifo->num;
|
||||
}
|
||||
|
||||
uint32_t fifo8_num_used(Fifo8 *fifo)
|
||||
{
|
||||
return fifo->num;
|
||||
}
|
||||
|
||||
const VMStateDescription vmstate_fifo8 = {
|
||||
.name = "Fifo8",
|
||||
.version_id = 1,
|
||||
|
|
Loading…
Reference in New Issue