425dda00df
all macros in ev5.hh to inline functions or constant typed variables and make them follow our style while we're at it. All of the stuff in this file actually belongs in the ISA traits code, but this is a first step at getting things done in the right manner. arch/alpha/alpha_memory.cc: arch/alpha/alpha_memory.hh: arch/alpha/ev5.cc: arch/alpha/isa_desc: dev/ns_gige.cc: kern/tru64/tru64_events.cc: deal with changes in ev5.hh arch/alpha/ev5.hh: Macros are nasty, so let's get rid of them. Convert all all macros to inline functions or constant typed variables. Make them follow our style while we're at it. All of the stuff in this file actually belongs in the ISA traits code, but this is a first step at getting things done in the right manner. arch/alpha/isa_traits.hh: move some of the ev5 specific code into the isa arch/alpha/vtophys.cc: base/remote_gdb.cc: deal with isa addition cpu/exec_context.hh: be less isa specific and use the isa traits to figure out what we can. dev/alpha_console.cc: dev/pciconfigall.cc: dev/tsunami_cchip.cc: dev/tsunami_io.cc: dev/tsunami_pchip.cc: dev/uart.cc: deal with changes in ev5.hh I don't believe this masking is actually necessary. We should look at removing it later. dev/ide_ctrl.cc: sort #includes deal with changes in ev5.hh --HG-- extra : convert_revision : c8a3adf0a4b1d198aefe38fc38b295abf289b08a
2580 lines
71 KiB
C++
2580 lines
71 KiB
C++
// -*- mode:c++ -*-
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Alpha ISA description file.
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Output include file directives.
|
|
//
|
|
|
|
output header {{
|
|
#include <sstream>
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
|
|
#include "cpu/static_inst.hh"
|
|
#include "mem/mem_req.hh" // some constructors use MemReq flags
|
|
}};
|
|
|
|
output decoder {{
|
|
#include "base/cprintf.hh"
|
|
#include "base/loader/symtab.hh"
|
|
#include "cpu/exec_context.hh" // for Jump::branchTarget()
|
|
|
|
#include <math.h>
|
|
#if defined(linux)
|
|
#include <fenv.h>
|
|
#endif
|
|
}};
|
|
|
|
output exec {{
|
|
#include <math.h>
|
|
#if defined(linux)
|
|
#include <fenv.h>
|
|
#endif
|
|
|
|
#ifdef FULL_SYSTEM
|
|
#include "arch/alpha/pseudo_inst.hh"
|
|
#endif
|
|
#include "cpu/base_cpu.hh"
|
|
#include "cpu/exetrace.hh"
|
|
#include "sim/sim_exit.hh"
|
|
}};
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Namespace statement. Everything below this line will be in the
|
|
// AlphaISAInst namespace.
|
|
//
|
|
|
|
|
|
namespace AlphaISA;
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Bitfield definitions.
|
|
//
|
|
|
|
// Universal (format-independent) fields
|
|
def bitfield OPCODE <31:26>;
|
|
def bitfield RA <25:21>;
|
|
def bitfield RB <20:16>;
|
|
|
|
// Memory format
|
|
def signed bitfield MEMDISP <15: 0>; // displacement
|
|
def bitfield MEMFUNC <15: 0>; // function code (same field, unsigned)
|
|
|
|
// Memory-format jumps
|
|
def bitfield JMPFUNC <15:14>; // function code (disp<15:14>)
|
|
def bitfield JMPHINT <13: 0>; // tgt Icache idx hint (disp<13:0>)
|
|
|
|
// Branch format
|
|
def signed bitfield BRDISP <20: 0>; // displacement
|
|
|
|
// Integer operate format(s>;
|
|
def bitfield INTIMM <20:13>; // integer immediate (literal)
|
|
def bitfield IMM <12:12>; // immediate flag
|
|
def bitfield INTFUNC <11: 5>; // function code
|
|
def bitfield RC < 4: 0>; // dest reg
|
|
|
|
// Floating-point operate format
|
|
def bitfield FA <25:21>;
|
|
def bitfield FB <20:16>;
|
|
def bitfield FP_FULLFUNC <15: 5>; // complete function code
|
|
def bitfield FP_TRAPMODE <15:13>; // trapping mode
|
|
def bitfield FP_ROUNDMODE <12:11>; // rounding mode
|
|
def bitfield FP_TYPEFUNC <10: 5>; // type+func: handiest for decoding
|
|
def bitfield FP_SRCTYPE <10: 9>; // source reg type
|
|
def bitfield FP_SHORTFUNC < 8: 5>; // short function code
|
|
def bitfield FP_SHORTFUNC_TOP2 <8:7>; // top 2 bits of short func code
|
|
def bitfield FC < 4: 0>; // dest reg
|
|
|
|
// PALcode format
|
|
def bitfield PALFUNC <25: 0>; // function code
|
|
|
|
// EV5 PAL instructions:
|
|
// HW_LD/HW_ST
|
|
def bitfield HW_LDST_PHYS <15>; // address is physical
|
|
def bitfield HW_LDST_ALT <14>; // use ALT_MODE IPR
|
|
def bitfield HW_LDST_WRTCK <13>; // HW_LD only: fault if no write acc
|
|
def bitfield HW_LDST_QUAD <12>; // size: 0=32b, 1=64b
|
|
def bitfield HW_LDST_VPTE <11>; // HW_LD only: is PTE fetch
|
|
def bitfield HW_LDST_LOCK <10>; // HW_LD only: is load locked
|
|
def bitfield HW_LDST_COND <10>; // HW_ST only: is store conditional
|
|
def signed bitfield HW_LDST_DISP <9:0>; // signed displacement
|
|
|
|
// HW_REI
|
|
def bitfield HW_REI_TYP <15:14>; // type: stalling vs. non-stallingk
|
|
def bitfield HW_REI_MBZ <13: 0>; // must be zero
|
|
|
|
// HW_MTPR/MW_MFPR
|
|
def bitfield HW_IPR_IDX <15:0>; // IPR index
|
|
|
|
// M5 instructions
|
|
def bitfield M5FUNC <7:0>;
|
|
|
|
def operand_types {{
|
|
'sb' : ('signed int', 8),
|
|
'ub' : ('unsigned int', 8),
|
|
'sw' : ('signed int', 16),
|
|
'uw' : ('unsigned int', 16),
|
|
'sl' : ('signed int', 32),
|
|
'ul' : ('unsigned int', 32),
|
|
'sq' : ('signed int', 64),
|
|
'uq' : ('unsigned int', 64),
|
|
'sf' : ('float', 32),
|
|
'df' : ('float', 64)
|
|
}};
|
|
|
|
def operands {{
|
|
# Int regs default to unsigned, but code should not count on this.
|
|
# For clarity, descriptions that depend on unsigned behavior should
|
|
# explicitly specify '.uq'.
|
|
'Ra': IntRegOperandTraits('uq', 'RA', 'IsInteger', 1),
|
|
'Rb': IntRegOperandTraits('uq', 'RB', 'IsInteger', 2),
|
|
'Rc': IntRegOperandTraits('uq', 'RC', 'IsInteger', 3),
|
|
'Fa': FloatRegOperandTraits('df', 'FA', 'IsFloating', 1),
|
|
'Fb': FloatRegOperandTraits('df', 'FB', 'IsFloating', 2),
|
|
'Fc': FloatRegOperandTraits('df', 'FC', 'IsFloating', 3),
|
|
'Mem': MemOperandTraits('uq', None,
|
|
('IsMemRef', 'IsLoad', 'IsStore'), 4),
|
|
'NPC': NPCOperandTraits('uq', None, ( None, None, 'IsControl' ), 4),
|
|
'Runiq': ControlRegOperandTraits('uq', 'Uniq', None, 1),
|
|
'FPCR': ControlRegOperandTraits('uq', 'Fpcr', None, 1),
|
|
# The next two are hacks for non-full-system call-pal emulation
|
|
'R0': IntRegOperandTraits('uq', '0', None, 1),
|
|
'R16': IntRegOperandTraits('uq', '16', None, 1)
|
|
}};
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Basic instruction classes/templates/formats etc.
|
|
//
|
|
|
|
output header {{
|
|
// uncomment the following to get SimpleScalar-compatible disassembly
|
|
// (useful for diffing output traces).
|
|
// #define SS_COMPATIBLE_DISASSEMBLY
|
|
|
|
/**
|
|
* Base class for all Alpha static instructions.
|
|
*/
|
|
class AlphaStaticInst : public StaticInst<AlphaISA>
|
|
{
|
|
protected:
|
|
|
|
/// Make AlphaISA register dependence tags directly visible in
|
|
/// this class and derived classes. Maybe these should really
|
|
/// live here and not in the AlphaISA namespace.
|
|
enum DependenceTags {
|
|
FP_Base_DepTag = AlphaISA::FP_Base_DepTag,
|
|
Fpcr_DepTag = AlphaISA::Fpcr_DepTag,
|
|
Uniq_DepTag = AlphaISA::Uniq_DepTag,
|
|
IPR_Base_DepTag = AlphaISA::IPR_Base_DepTag
|
|
};
|
|
|
|
/// Constructor.
|
|
AlphaStaticInst(const char *mnem, MachInst _machInst,
|
|
OpClass __opClass)
|
|
: StaticInst<AlphaISA>(mnem, _machInst, __opClass)
|
|
{
|
|
}
|
|
|
|
/// Print a register name for disassembly given the unique
|
|
/// dependence tag number (FP or int).
|
|
void printReg(std::ostream &os, int reg);
|
|
|
|
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
|
|
};
|
|
}};
|
|
|
|
output decoder {{
|
|
void
|
|
AlphaStaticInst::printReg(std::ostream &os, int reg)
|
|
{
|
|
if (reg < FP_Base_DepTag) {
|
|
ccprintf(os, "r%d", reg);
|
|
}
|
|
else {
|
|
ccprintf(os, "f%d", reg - FP_Base_DepTag);
|
|
}
|
|
}
|
|
|
|
std::string
|
|
AlphaStaticInst::generateDisassembly(Addr pc, const SymbolTable *symtab)
|
|
{
|
|
std::stringstream ss;
|
|
|
|
ccprintf(ss, "%-10s ", mnemonic);
|
|
|
|
// just print the first two source regs... if there's
|
|
// a third one, it's a read-modify-write dest (Rc),
|
|
// e.g. for CMOVxx
|
|
if (_numSrcRegs > 0) {
|
|
printReg(ss, _srcRegIdx[0]);
|
|
}
|
|
if (_numSrcRegs > 1) {
|
|
ss << ",";
|
|
printReg(ss, _srcRegIdx[1]);
|
|
}
|
|
|
|
// just print the first dest... if there's a second one,
|
|
// it's generally implicit
|
|
if (_numDestRegs > 0) {
|
|
if (_numSrcRegs > 0)
|
|
ss << ",";
|
|
printReg(ss, _destRegIdx[0]);
|
|
}
|
|
|
|
return ss.str();
|
|
}
|
|
}};
|
|
|
|
// Declarations for execute() methods.
|
|
def template BasicExecDeclare {{
|
|
Fault execute(%(CPU_exec_context)s *, Trace::InstRecord *);
|
|
}};
|
|
|
|
// Basic instruction class declaration template.
|
|
def template BasicDeclare {{
|
|
/**
|
|
* Static instruction class for "%(mnemonic)s".
|
|
*/
|
|
class %(class_name)s : public %(base_class)s
|
|
{
|
|
public:
|
|
/// Constructor.
|
|
%(class_name)s(MachInst machInst);
|
|
|
|
%(BasicExecDeclare)s
|
|
};
|
|
}};
|
|
|
|
// Basic instruction class constructor template.
|
|
def template BasicConstructor {{
|
|
inline %(class_name)s::%(class_name)s(MachInst machInst)
|
|
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s)
|
|
{
|
|
%(constructor)s;
|
|
}
|
|
}};
|
|
|
|
// Basic instruction class execute method template.
|
|
def template BasicExecute {{
|
|
Fault %(class_name)s::execute(%(CPU_exec_context)s *xc,
|
|
Trace::InstRecord *traceData)
|
|
{
|
|
Fault fault = No_Fault;
|
|
|
|
%(fp_enable_check)s;
|
|
%(op_decl)s;
|
|
%(op_rd)s;
|
|
%(code)s;
|
|
|
|
if (fault == No_Fault) {
|
|
%(op_wb)s;
|
|
}
|
|
|
|
return fault;
|
|
}
|
|
}};
|
|
|
|
// Basic decode template.
|
|
def template BasicDecode {{
|
|
return new %(class_name)s(machInst);
|
|
}};
|
|
|
|
// Basic decode template, passing mnemonic in as string arg to constructor.
|
|
def template BasicDecodeWithMnemonic {{
|
|
return new %(class_name)s("%(mnemonic)s", machInst);
|
|
}};
|
|
|
|
// The most basic instruction format... used only for a few misc. insts
|
|
def format BasicOperate(code, *flags) {{
|
|
iop = InstObjParams(name, Name, 'AlphaStaticInst', CodeBlock(code), flags)
|
|
header_output = BasicDeclare.subst(iop)
|
|
decoder_output = BasicConstructor.subst(iop)
|
|
decode_block = BasicDecode.subst(iop)
|
|
exec_output = BasicExecute.subst(iop)
|
|
}};
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Nop
|
|
//
|
|
|
|
output header {{
|
|
/**
|
|
* Static instruction class for no-ops. This is a leaf class.
|
|
*/
|
|
class Nop : public AlphaStaticInst
|
|
{
|
|
/// Disassembly of original instruction.
|
|
const std::string originalDisassembly;
|
|
|
|
public:
|
|
/// Constructor
|
|
Nop(const std::string _originalDisassembly, MachInst _machInst)
|
|
: AlphaStaticInst("nop", _machInst, No_OpClass),
|
|
originalDisassembly(_originalDisassembly)
|
|
{
|
|
flags[IsNop] = true;
|
|
}
|
|
|
|
~Nop() { }
|
|
|
|
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
|
|
|
|
%(BasicExecDeclare)s
|
|
};
|
|
}};
|
|
|
|
output decoder {{
|
|
std::string Nop::generateDisassembly(Addr pc, const SymbolTable *symtab)
|
|
{
|
|
#ifdef SS_COMPATIBLE_DISASSEMBLY
|
|
return originalDisassembly;
|
|
#else
|
|
return csprintf("%-10s (%s)", "nop", originalDisassembly);
|
|
#endif
|
|
}
|
|
|
|
/// Helper function for decoding nops. Substitute Nop object
|
|
/// for original inst passed in as arg (and delete latter).
|
|
inline
|
|
AlphaStaticInst *
|
|
makeNop(AlphaStaticInst *inst)
|
|
{
|
|
AlphaStaticInst *nop = new Nop(inst->disassemble(0), inst->machInst);
|
|
delete inst;
|
|
return nop;
|
|
}
|
|
}};
|
|
|
|
output exec {{
|
|
Fault
|
|
Nop::execute(%(CPU_exec_context)s *, Trace::InstRecord *)
|
|
{
|
|
return No_Fault;
|
|
}
|
|
}};
|
|
|
|
// integer & FP operate instructions use Rc as dest, so check for
|
|
// Rc == 31 to detect nops
|
|
def template OperateNopCheckDecode {{
|
|
{
|
|
AlphaStaticInst *i = new %(class_name)s(machInst);
|
|
if (RC == 31) {
|
|
i = makeNop(i);
|
|
}
|
|
return i;
|
|
}
|
|
}};
|
|
|
|
// Like BasicOperate format, but generates NOP if RC/FC == 31
|
|
def format BasicOperateWithNopCheck(code, *opt_args) {{
|
|
iop = InstObjParams(name, Name, 'AlphaStaticInst', CodeBlock(code),
|
|
opt_args)
|
|
header_output = BasicDeclare.subst(iop)
|
|
decoder_output = BasicConstructor.subst(iop)
|
|
decode_block = OperateNopCheckDecode.subst(iop)
|
|
exec_output = BasicExecute.subst(iop)
|
|
}};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Integer operate instructions
|
|
//
|
|
|
|
output header {{
|
|
/**
|
|
* Base class for integer immediate instructions.
|
|
*/
|
|
class IntegerImm : public AlphaStaticInst
|
|
{
|
|
protected:
|
|
/// Immediate operand value (unsigned 8-bit int).
|
|
uint8_t imm;
|
|
|
|
/// Constructor
|
|
IntegerImm(const char *mnem, MachInst _machInst, OpClass __opClass)
|
|
: AlphaStaticInst(mnem, _machInst, __opClass), imm(INTIMM)
|
|
{
|
|
}
|
|
|
|
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
|
|
};
|
|
}};
|
|
|
|
output decoder {{
|
|
std::string
|
|
IntegerImm::generateDisassembly(Addr pc, const SymbolTable *symtab)
|
|
{
|
|
std::stringstream ss;
|
|
|
|
ccprintf(ss, "%-10s ", mnemonic);
|
|
|
|
// just print the first source reg... if there's
|
|
// a second one, it's a read-modify-write dest (Rc),
|
|
// e.g. for CMOVxx
|
|
if (_numSrcRegs > 0) {
|
|
printReg(ss, _srcRegIdx[0]);
|
|
ss << ",";
|
|
}
|
|
|
|
ss << (int)imm;
|
|
|
|
if (_numDestRegs > 0) {
|
|
ss << ",";
|
|
printReg(ss, _destRegIdx[0]);
|
|
}
|
|
|
|
return ss.str();
|
|
}
|
|
}};
|
|
|
|
|
|
def template RegOrImmDecode {{
|
|
{
|
|
AlphaStaticInst *i =
|
|
(IMM) ? (AlphaStaticInst *)new %(class_name)sImm(machInst)
|
|
: (AlphaStaticInst *)new %(class_name)s(machInst);
|
|
if (RC == 31) {
|
|
i = makeNop(i);
|
|
}
|
|
return i;
|
|
}
|
|
}};
|
|
|
|
// Primary format for integer operate instructions:
|
|
// - Generates both reg-reg and reg-imm versions if Rb_or_imm is used.
|
|
// - Generates NOP if RC == 31.
|
|
def format IntegerOperate(code, *opt_flags) {{
|
|
# If the code block contains 'Rb_or_imm', we define two instructions,
|
|
# one using 'Rb' and one using 'imm', and have the decoder select
|
|
# the right one.
|
|
uses_imm = (code.find('Rb_or_imm') != -1)
|
|
if uses_imm:
|
|
orig_code = code
|
|
# base code is reg version:
|
|
# rewrite by substituting 'Rb' for 'Rb_or_imm'
|
|
code = re.sub(r'Rb_or_imm', 'Rb', orig_code)
|
|
# generate immediate version by substituting 'imm'
|
|
# note that imm takes no extenstion, so we extend
|
|
# the regexp to replace any extension as well
|
|
imm_code = re.sub(r'Rb_or_imm(\.\w+)?', 'imm', orig_code)
|
|
|
|
# generate declaration for register version
|
|
cblk = CodeBlock(code)
|
|
iop = InstObjParams(name, Name, 'AlphaStaticInst', cblk, opt_flags)
|
|
header_output = BasicDeclare.subst(iop)
|
|
decoder_output = BasicConstructor.subst(iop)
|
|
exec_output = BasicExecute.subst(iop)
|
|
|
|
if uses_imm:
|
|
# append declaration for imm version
|
|
imm_cblk = CodeBlock(imm_code)
|
|
imm_iop = InstObjParams(name, Name + 'Imm', 'IntegerImm', imm_cblk,
|
|
opt_flags)
|
|
header_output += BasicDeclare.subst(imm_iop)
|
|
decoder_output += BasicConstructor.subst(imm_iop)
|
|
exec_output += BasicExecute.subst(imm_iop)
|
|
# decode checks IMM bit to pick correct version
|
|
decode_block = RegOrImmDecode.subst(iop)
|
|
else:
|
|
# no imm version: just check for nop
|
|
decode_block = OperateNopCheckDecode.subst(iop)
|
|
}};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Floating-point instructions
|
|
//
|
|
// Note that many FP-type instructions which do not support all the
|
|
// various rounding & trapping modes use the simpler format
|
|
// BasicOperateWithNopCheck.
|
|
//
|
|
|
|
output exec {{
|
|
/// Check "FP enabled" machine status bit. Called when executing any FP
|
|
/// instruction in full-system mode.
|
|
/// @retval Full-system mode: No_Fault if FP is enabled, Fen_Fault
|
|
/// if not. Non-full-system mode: always returns No_Fault.
|
|
#ifdef FULL_SYSTEM
|
|
inline Fault checkFpEnableFault(%(CPU_exec_context)s *xc)
|
|
{
|
|
Fault fault = No_Fault; // dummy... this ipr access should not fault
|
|
if (!EV5::ICSR_FPE(xc->readIpr(AlphaISA::IPR_ICSR, fault))) {
|
|
fault = Fen_Fault;
|
|
}
|
|
return fault;
|
|
}
|
|
#else
|
|
inline Fault checkFpEnableFault(%(CPU_exec_context)s *xc)
|
|
{
|
|
return No_Fault;
|
|
}
|
|
#endif
|
|
}};
|
|
|
|
output header {{
|
|
/**
|
|
* Base class for general floating-point instructions. Includes
|
|
* support for various Alpha rounding and trapping modes. Only FP
|
|
* instructions that require this support are derived from this
|
|
* class; the rest derive directly from AlphaStaticInst.
|
|
*/
|
|
class AlphaFP : public AlphaStaticInst
|
|
{
|
|
public:
|
|
/// Alpha FP rounding modes.
|
|
enum RoundingMode {
|
|
Chopped = 0, ///< round toward zero
|
|
Minus_Infinity = 1, ///< round toward minus infinity
|
|
Normal = 2, ///< round to nearest (default)
|
|
Dynamic = 3, ///< use FPCR setting (in instruction)
|
|
Plus_Infinity = 3 ///< round to plus inifinity (in FPCR)
|
|
};
|
|
|
|
/// Alpha FP trapping modes.
|
|
/// For instructions that produce integer results, the
|
|
/// "Underflow Enable" modes really mean "Overflow Enable", and
|
|
/// the assembly modifier is V rather than U.
|
|
enum TrappingMode {
|
|
/// default: nothing enabled
|
|
Imprecise = 0, ///< no modifier
|
|
/// underflow/overflow traps enabled, inexact disabled
|
|
Underflow_Imprecise = 1, ///< /U or /V
|
|
Underflow_Precise = 5, ///< /SU or /SV
|
|
/// underflow/overflow and inexact traps enabled
|
|
Underflow_Inexact_Precise = 7 ///< /SUI or /SVI
|
|
};
|
|
|
|
protected:
|
|
#if defined(linux)
|
|
static const int alphaToC99RoundingMode[];
|
|
#endif
|
|
|
|
/// Map enum RoundingMode values to disassembly suffixes.
|
|
static const char *roundingModeSuffix[];
|
|
/// Map enum TrappingMode values to FP disassembly suffixes.
|
|
static const char *fpTrappingModeSuffix[];
|
|
/// Map enum TrappingMode values to integer disassembly suffixes.
|
|
static const char *intTrappingModeSuffix[];
|
|
|
|
/// This instruction's rounding mode.
|
|
RoundingMode roundingMode;
|
|
/// This instruction's trapping mode.
|
|
TrappingMode trappingMode;
|
|
|
|
/// Constructor
|
|
AlphaFP(const char *mnem, MachInst _machInst, OpClass __opClass)
|
|
: AlphaStaticInst(mnem, _machInst, __opClass),
|
|
roundingMode((enum RoundingMode)FP_ROUNDMODE),
|
|
trappingMode((enum TrappingMode)FP_TRAPMODE)
|
|
{
|
|
if (trappingMode != Imprecise) {
|
|
warn("precise FP traps unimplemented\n");
|
|
}
|
|
}
|
|
|
|
#if defined(linux)
|
|
int getC99RoundingMode(uint64_t fpcr_val);
|
|
#endif
|
|
|
|
// This differs from the AlphaStaticInst version only in
|
|
// printing suffixes for non-default rounding & trapping modes.
|
|
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
|
|
};
|
|
|
|
}};
|
|
|
|
|
|
def template FloatingPointDecode {{
|
|
{
|
|
bool fast = (FP_TRAPMODE == AlphaFP::Imprecise
|
|
&& FP_ROUNDMODE == AlphaFP::Normal);
|
|
AlphaStaticInst *i =
|
|
fast ? (AlphaStaticInst *)new %(class_name)sFast(machInst) :
|
|
(AlphaStaticInst *)new %(class_name)sGeneral(machInst);
|
|
|
|
if (FC == 31) {
|
|
i = makeNop(i);
|
|
}
|
|
|
|
return i;
|
|
}
|
|
}};
|
|
|
|
output decoder {{
|
|
#if defined(linux)
|
|
int
|
|
AlphaFP::getC99RoundingMode(uint64_t fpcr_val)
|
|
{
|
|
if (roundingMode == Dynamic) {
|
|
return alphaToC99RoundingMode[bits(fpcr_val, 59, 58)];
|
|
}
|
|
else {
|
|
return alphaToC99RoundingMode[roundingMode];
|
|
}
|
|
}
|
|
#endif
|
|
|
|
std::string
|
|
AlphaFP::generateDisassembly(Addr pc, const SymbolTable *symtab)
|
|
{
|
|
std::string mnem_str(mnemonic);
|
|
|
|
#ifndef SS_COMPATIBLE_DISASSEMBLY
|
|
std::string suffix("");
|
|
suffix += ((_destRegIdx[0] >= FP_Base_DepTag)
|
|
? fpTrappingModeSuffix[trappingMode]
|
|
: intTrappingModeSuffix[trappingMode]);
|
|
suffix += roundingModeSuffix[roundingMode];
|
|
|
|
if (suffix != "") {
|
|
mnem_str = csprintf("%s/%s", mnemonic, suffix);
|
|
}
|
|
#endif
|
|
|
|
std::stringstream ss;
|
|
ccprintf(ss, "%-10s ", mnem_str.c_str());
|
|
|
|
// just print the first two source regs... if there's
|
|
// a third one, it's a read-modify-write dest (Rc),
|
|
// e.g. for CMOVxx
|
|
if (_numSrcRegs > 0) {
|
|
printReg(ss, _srcRegIdx[0]);
|
|
}
|
|
if (_numSrcRegs > 1) {
|
|
ss << ",";
|
|
printReg(ss, _srcRegIdx[1]);
|
|
}
|
|
|
|
// just print the first dest... if there's a second one,
|
|
// it's generally implicit
|
|
if (_numDestRegs > 0) {
|
|
if (_numSrcRegs > 0)
|
|
ss << ",";
|
|
printReg(ss, _destRegIdx[0]);
|
|
}
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
#if defined(linux)
|
|
const int AlphaFP::alphaToC99RoundingMode[] = {
|
|
FE_TOWARDZERO, // Chopped
|
|
FE_DOWNWARD, // Minus_Infinity
|
|
FE_TONEAREST, // Normal
|
|
FE_UPWARD // Dynamic in inst, Plus_Infinity in FPCR
|
|
};
|
|
#endif
|
|
|
|
const char *AlphaFP::roundingModeSuffix[] = { "c", "m", "", "d" };
|
|
// mark invalid trapping modes, but don't fail on them, because
|
|
// you could decode anything on a misspeculated path
|
|
const char *AlphaFP::fpTrappingModeSuffix[] =
|
|
{ "", "u", "INVTM2", "INVTM3", "INVTM4", "su", "INVTM6", "sui" };
|
|
const char *AlphaFP::intTrappingModeSuffix[] =
|
|
{ "", "v", "INVTM2", "INVTM3", "INVTM4", "sv", "INVTM6", "svi" };
|
|
}};
|
|
|
|
// General format for floating-point operate instructions:
|
|
// - Checks trapping and rounding mode flags. Trapping modes
|
|
// currently unimplemented (will fail).
|
|
// - Generates NOP if FC == 31.
|
|
def format FloatingPointOperate(code, *opt_args) {{
|
|
iop = InstObjParams(name, Name, 'AlphaFP', CodeBlock(code), opt_args)
|
|
decode_block = FloatingPointDecode.subst(iop)
|
|
|
|
fast_iop = InstObjParams(name, Name + 'Fast', 'AlphaFP',
|
|
CodeBlock(code), opt_args)
|
|
header_output = BasicDeclare.subst(fast_iop)
|
|
decoder_output = BasicConstructor.subst(fast_iop)
|
|
exec_output = BasicExecute.subst(fast_iop)
|
|
|
|
gen_code_prefix = r'''
|
|
#if defined(linux)
|
|
fesetround(getC99RoundingMode(xc->readFpcr()));
|
|
#endif
|
|
'''
|
|
gen_code_suffix = r'''
|
|
#if defined(linux)
|
|
fesetround(FE_TONEAREST);
|
|
#endif
|
|
'''
|
|
|
|
gen_iop = InstObjParams(name, Name + 'General', 'AlphaFP',
|
|
CodeBlock(gen_code_prefix + code + gen_code_suffix), opt_args)
|
|
header_output += BasicDeclare.subst(gen_iop)
|
|
decoder_output += BasicConstructor.subst(gen_iop)
|
|
exec_output += BasicExecute.subst(gen_iop)
|
|
}};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Memory-format instructions: LoadAddress, Load, Store
|
|
//
|
|
|
|
output header {{
|
|
/**
|
|
* Base class for general Alpha memory-format instructions.
|
|
*/
|
|
class Memory : public AlphaStaticInst
|
|
{
|
|
protected:
|
|
|
|
/// Memory request flags. See mem_req_base.hh.
|
|
unsigned memAccessFlags;
|
|
/// Pointer to EAComp object.
|
|
const StaticInstPtr<AlphaISA> eaCompPtr;
|
|
/// Pointer to MemAcc object.
|
|
const StaticInstPtr<AlphaISA> memAccPtr;
|
|
|
|
/// Constructor
|
|
Memory(const char *mnem, MachInst _machInst, OpClass __opClass,
|
|
StaticInstPtr<AlphaISA> _eaCompPtr = nullStaticInstPtr,
|
|
StaticInstPtr<AlphaISA> _memAccPtr = nullStaticInstPtr)
|
|
: AlphaStaticInst(mnem, _machInst, __opClass),
|
|
memAccessFlags(0), eaCompPtr(_eaCompPtr), memAccPtr(_memAccPtr)
|
|
{
|
|
}
|
|
|
|
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
|
|
|
|
public:
|
|
|
|
const StaticInstPtr<AlphaISA> &eaCompInst() const { return eaCompPtr; }
|
|
const StaticInstPtr<AlphaISA> &memAccInst() const { return memAccPtr; }
|
|
};
|
|
|
|
/**
|
|
* Base class for memory-format instructions using a 32-bit
|
|
* displacement (i.e. most of them).
|
|
*/
|
|
class MemoryDisp32 : public Memory
|
|
{
|
|
protected:
|
|
/// Displacement for EA calculation (signed).
|
|
int32_t disp;
|
|
|
|
/// Constructor.
|
|
MemoryDisp32(const char *mnem, MachInst _machInst, OpClass __opClass,
|
|
StaticInstPtr<AlphaISA> _eaCompPtr = nullStaticInstPtr,
|
|
StaticInstPtr<AlphaISA> _memAccPtr = nullStaticInstPtr)
|
|
: Memory(mnem, _machInst, __opClass, _eaCompPtr, _memAccPtr),
|
|
disp(MEMDISP)
|
|
{
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Base class for a few miscellaneous memory-format insts
|
|
* that don't interpret the disp field: wh64, fetch, fetch_m, ecb.
|
|
* None of these instructions has a destination register either.
|
|
*/
|
|
class MemoryNoDisp : public Memory
|
|
{
|
|
protected:
|
|
/// Constructor
|
|
MemoryNoDisp(const char *mnem, MachInst _machInst, OpClass __opClass,
|
|
StaticInstPtr<AlphaISA> _eaCompPtr,
|
|
StaticInstPtr<AlphaISA> _memAccPtr)
|
|
: Memory(mnem, _machInst, __opClass, _eaCompPtr, _memAccPtr)
|
|
{
|
|
}
|
|
|
|
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
|
|
};
|
|
|
|
|
|
/**
|
|
* Base class for "fake" effective-address computation
|
|
* instructions returnded by eaCompInst().
|
|
*/
|
|
class EACompBase : public AlphaStaticInst
|
|
{
|
|
public:
|
|
/// Constructor
|
|
EACompBase(MachInst machInst)
|
|
: AlphaStaticInst("(eacomp)", machInst, IntAluOp)
|
|
{
|
|
}
|
|
|
|
%(BasicExecDeclare)s
|
|
};
|
|
|
|
/**
|
|
* Base class for "fake" memory-access instructions returnded by
|
|
* memAccInst().
|
|
*/
|
|
class MemAccBase : public AlphaStaticInst
|
|
{
|
|
public:
|
|
/// Constructor
|
|
MemAccBase(MachInst machInst, OpClass __opClass)
|
|
: AlphaStaticInst("(memacc)", machInst, __opClass)
|
|
{
|
|
}
|
|
|
|
%(BasicExecDeclare)s
|
|
};
|
|
|
|
}};
|
|
|
|
|
|
output decoder {{
|
|
std::string
|
|
Memory::generateDisassembly(Addr pc, const SymbolTable *symtab)
|
|
{
|
|
return csprintf("%-10s %c%d,%d(r%d)", mnemonic,
|
|
flags[IsFloating] ? 'f' : 'r', RA, MEMDISP, RB);
|
|
}
|
|
|
|
std::string
|
|
MemoryNoDisp::generateDisassembly(Addr pc, const SymbolTable *symtab)
|
|
{
|
|
return csprintf("%-10s (r%d)", mnemonic, RB);
|
|
}
|
|
}};
|
|
|
|
output exec {{
|
|
Fault
|
|
EACompBase::execute(%(CPU_exec_context)s *, Trace::InstRecord *)
|
|
{
|
|
panic("attempt to execute eacomp");
|
|
}
|
|
|
|
Fault
|
|
MemAccBase::execute(%(CPU_exec_context)s *, Trace::InstRecord *)
|
|
{
|
|
panic("attempt to execute memacc");
|
|
}
|
|
}};
|
|
|
|
|
|
def format LoadAddress(code) {{
|
|
iop = InstObjParams(name, Name, 'MemoryDisp32', CodeBlock(code))
|
|
header_output = BasicDeclare.subst(iop)
|
|
decoder_output = BasicConstructor.subst(iop)
|
|
decode_block = BasicDecode.subst(iop)
|
|
exec_output = BasicExecute.subst(iop)
|
|
}};
|
|
|
|
|
|
def template LoadStoreDeclare {{
|
|
/**
|
|
* Static instruction class for "%(mnemonic)s".
|
|
*/
|
|
class %(class_name)s : public %(base_class)s
|
|
{
|
|
protected:
|
|
|
|
/**
|
|
* "Fake" effective address computation class for "%(mnemonic)s".
|
|
*/
|
|
class EAComp : public EACompBase
|
|
{
|
|
public:
|
|
/// Constructor
|
|
EAComp(MachInst machInst);
|
|
};
|
|
|
|
/**
|
|
* "Fake" memory access instruction class for "%(mnemonic)s".
|
|
*/
|
|
class MemAcc : public MemAccBase
|
|
{
|
|
public:
|
|
/// Constructor
|
|
MemAcc(MachInst machInst);
|
|
};
|
|
|
|
public:
|
|
|
|
/// Constructor.
|
|
%(class_name)s(MachInst machInst);
|
|
|
|
%(BasicExecDeclare)s
|
|
};
|
|
}};
|
|
|
|
def template LoadStoreConstructor {{
|
|
inline %(class_name)s::EAComp::EAComp(MachInst machInst)
|
|
: EACompBase(machInst)
|
|
{
|
|
%(ea_constructor)s;
|
|
}
|
|
|
|
inline %(class_name)s::MemAcc::MemAcc(MachInst machInst)
|
|
: MemAccBase(machInst, %(op_class)s)
|
|
{
|
|
%(memacc_constructor)s;
|
|
}
|
|
|
|
inline %(class_name)s::%(class_name)s(MachInst machInst)
|
|
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
|
|
new EAComp(machInst), new MemAcc(machInst))
|
|
{
|
|
%(constructor)s;
|
|
}
|
|
}};
|
|
|
|
def template LoadStoreExecute {{
|
|
Fault %(class_name)s::execute(%(CPU_exec_context)s *xc,
|
|
Trace::InstRecord *traceData)
|
|
{
|
|
Addr EA;
|
|
Fault fault = No_Fault;
|
|
|
|
%(fp_enable_check)s;
|
|
%(op_decl)s;
|
|
%(op_nonmem_rd)s;
|
|
%(ea_code)s;
|
|
|
|
if (fault == No_Fault) {
|
|
%(op_mem_rd)s;
|
|
%(memacc_code)s;
|
|
}
|
|
|
|
if (fault == No_Fault) {
|
|
%(op_mem_wb)s;
|
|
}
|
|
|
|
if (fault == No_Fault) {
|
|
%(postacc_code)s;
|
|
}
|
|
|
|
if (fault == No_Fault) {
|
|
%(op_nonmem_wb)s;
|
|
}
|
|
|
|
return fault;
|
|
}
|
|
}};
|
|
|
|
|
|
def template PrefetchExecute {{
|
|
Fault %(class_name)s::execute(%(CPU_exec_context)s *xc,
|
|
Trace::InstRecord *traceData)
|
|
{
|
|
Addr EA;
|
|
Fault fault = No_Fault;
|
|
|
|
%(fp_enable_check)s;
|
|
%(op_decl)s;
|
|
%(op_nonmem_rd)s;
|
|
%(ea_code)s;
|
|
|
|
if (fault == No_Fault) {
|
|
xc->prefetch(EA, memAccessFlags);
|
|
}
|
|
|
|
return No_Fault;
|
|
}
|
|
}};
|
|
|
|
// load instructions use Ra as dest, so check for
|
|
// Ra == 31 to detect nops
|
|
def template LoadNopCheckDecode {{
|
|
{
|
|
AlphaStaticInst *i = new %(class_name)s(machInst);
|
|
if (RA == 31) {
|
|
i = makeNop(i);
|
|
}
|
|
return i;
|
|
}
|
|
}};
|
|
|
|
|
|
// for some load instructions, Ra == 31 indicates a prefetch (not a nop)
|
|
def template LoadPrefetchCheckDecode {{
|
|
{
|
|
if (RA != 31) {
|
|
return new %(class_name)s(machInst);
|
|
}
|
|
else {
|
|
return new %(class_name)sPrefetch(machInst);
|
|
}
|
|
}
|
|
}};
|
|
|
|
|
|
let {{
|
|
def LoadStoreBase(name, Name, ea_code, memacc_code, postacc_code = '',
|
|
base_class = 'MemoryDisp32', flags = [],
|
|
decode_template = BasicDecode,
|
|
exec_template = LoadStoreExecute):
|
|
# Segregate flags into instruction flags (handled by InstObjParams)
|
|
# and memory access flags (handled here).
|
|
|
|
# Would be nice to autogenerate this list, but oh well.
|
|
valid_mem_flags = ['LOCKED', 'NO_FAULT', 'EVICT_NEXT', 'PF_EXCLUSIVE']
|
|
inst_flags = []
|
|
mem_flags = []
|
|
for f in flags:
|
|
if f in valid_mem_flags:
|
|
mem_flags.append(f)
|
|
else:
|
|
inst_flags.append(f)
|
|
|
|
ea_cblk = CodeBlock(ea_code)
|
|
memacc_cblk = CodeBlock(memacc_code)
|
|
postacc_cblk = CodeBlock(postacc_code)
|
|
|
|
cblk = CodeBlock(ea_code + memacc_code + postacc_code)
|
|
iop = InstObjParams(name, Name, base_class, cblk, inst_flags)
|
|
|
|
iop.ea_constructor = ea_cblk.constructor
|
|
iop.ea_code = ea_cblk.code
|
|
iop.memacc_constructor = memacc_cblk.constructor
|
|
iop.memacc_code = memacc_cblk.code
|
|
iop.postacc_code = postacc_cblk.code
|
|
|
|
mem_flags = string.join(mem_flags, '|')
|
|
if mem_flags != '':
|
|
iop.constructor += '\n\tmemAccessFlags = ' + mem_flags + ';'
|
|
|
|
# (header_output, decoder_output, decode_block, exec_output)
|
|
return (LoadStoreDeclare.subst(iop), LoadStoreConstructor.subst(iop),
|
|
decode_template.subst(iop), exec_template.subst(iop))
|
|
}};
|
|
|
|
|
|
def format LoadOrNop(ea_code, memacc_code, *flags) {{
|
|
(header_output, decoder_output, decode_block, exec_output) = \
|
|
LoadStoreBase(name, Name, ea_code, memacc_code, flags = flags,
|
|
decode_template = LoadNopCheckDecode)
|
|
}};
|
|
|
|
|
|
// Note that the flags passed in apply only to the prefetch version
|
|
def format LoadOrPrefetch(ea_code, memacc_code, *pf_flags) {{
|
|
# declare the load instruction object and generate the decode block
|
|
(header_output, decoder_output, decode_block, exec_output) = \
|
|
LoadStoreBase(name, Name, ea_code, memacc_code,
|
|
decode_template = LoadPrefetchCheckDecode)
|
|
|
|
# Declare the prefetch instruction object.
|
|
|
|
# convert flags from tuple to list to make them mutable
|
|
pf_flags = list(pf_flags) + ['IsMemRef', 'IsLoad', 'IsDataPrefetch', 'MemReadOp', 'NO_FAULT']
|
|
|
|
(pf_header_output, pf_decoder_output, _, pf_exec_output) = \
|
|
LoadStoreBase(name, Name + 'Prefetch', ea_code, '',
|
|
flags = pf_flags, exec_template = PrefetchExecute)
|
|
|
|
header_output += pf_header_output
|
|
decoder_output += pf_decoder_output
|
|
exec_output += pf_exec_output
|
|
}};
|
|
|
|
|
|
def format Store(ea_code, memacc_code, *flags) {{
|
|
(header_output, decoder_output, decode_block, exec_output) = \
|
|
LoadStoreBase(name, Name, ea_code, memacc_code, flags = flags)
|
|
}};
|
|
|
|
|
|
def format StoreCond(ea_code, memacc_code, postacc_code, *flags) {{
|
|
(header_output, decoder_output, decode_block, exec_output) = \
|
|
LoadStoreBase(name, Name, ea_code, memacc_code, postacc_code,
|
|
flags = flags)
|
|
}};
|
|
|
|
|
|
// Use 'MemoryNoDisp' as base: for wh64, fetch, ecb
|
|
def format MiscPrefetch(ea_code, memacc_code, *flags) {{
|
|
(header_output, decoder_output, decode_block, exec_output) = \
|
|
LoadStoreBase(name, Name, ea_code, memacc_code, flags = flags,
|
|
base_class = 'MemoryNoDisp')
|
|
}};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Control transfer instructions
|
|
//
|
|
|
|
output header {{
|
|
|
|
/**
|
|
* Base class for instructions whose disassembly is not purely a
|
|
* function of the machine instruction (i.e., it depends on the
|
|
* PC). This class overrides the disassemble() method to check
|
|
* the PC and symbol table values before re-using a cached
|
|
* disassembly string. This is necessary for branches and jumps,
|
|
* where the disassembly string includes the target address (which
|
|
* may depend on the PC and/or symbol table).
|
|
*/
|
|
class PCDependentDisassembly : public AlphaStaticInst
|
|
{
|
|
protected:
|
|
/// Cached program counter from last disassembly
|
|
Addr cachedPC;
|
|
/// Cached symbol table pointer from last disassembly
|
|
const SymbolTable *cachedSymtab;
|
|
|
|
/// Constructor
|
|
PCDependentDisassembly(const char *mnem, MachInst _machInst,
|
|
OpClass __opClass)
|
|
: AlphaStaticInst(mnem, _machInst, __opClass),
|
|
cachedPC(0), cachedSymtab(0)
|
|
{
|
|
}
|
|
|
|
const std::string &disassemble(Addr pc, const SymbolTable *symtab);
|
|
};
|
|
|
|
/**
|
|
* Base class for branches (PC-relative control transfers),
|
|
* conditional or unconditional.
|
|
*/
|
|
class Branch : public PCDependentDisassembly
|
|
{
|
|
protected:
|
|
/// Displacement to target address (signed).
|
|
int32_t disp;
|
|
|
|
/// Constructor.
|
|
Branch(const char *mnem, MachInst _machInst, OpClass __opClass)
|
|
: PCDependentDisassembly(mnem, _machInst, __opClass),
|
|
disp(BRDISP << 2)
|
|
{
|
|
}
|
|
|
|
Addr branchTarget(Addr branchPC) const;
|
|
|
|
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
|
|
};
|
|
|
|
/**
|
|
* Base class for jumps (register-indirect control transfers). In
|
|
* the Alpha ISA, these are always unconditional.
|
|
*/
|
|
class Jump : public PCDependentDisassembly
|
|
{
|
|
protected:
|
|
|
|
/// Displacement to target address (signed).
|
|
int32_t disp;
|
|
|
|
public:
|
|
/// Constructor
|
|
Jump(const char *mnem, MachInst _machInst, OpClass __opClass)
|
|
: PCDependentDisassembly(mnem, _machInst, __opClass),
|
|
disp(BRDISP)
|
|
{
|
|
}
|
|
|
|
Addr branchTarget(ExecContext *xc) const;
|
|
|
|
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
|
|
};
|
|
}};
|
|
|
|
output decoder {{
|
|
Addr
|
|
Branch::branchTarget(Addr branchPC) const
|
|
{
|
|
return branchPC + 4 + disp;
|
|
}
|
|
|
|
Addr
|
|
Jump::branchTarget(ExecContext *xc) const
|
|
{
|
|
Addr NPC = xc->readPC() + 4;
|
|
uint64_t Rb = xc->readIntReg(_srcRegIdx[0]);
|
|
return (Rb & ~3) | (NPC & 1);
|
|
}
|
|
|
|
const std::string &
|
|
PCDependentDisassembly::disassemble(Addr pc, const SymbolTable *symtab)
|
|
{
|
|
if (!cachedDisassembly ||
|
|
pc != cachedPC || symtab != cachedSymtab)
|
|
{
|
|
if (cachedDisassembly)
|
|
delete cachedDisassembly;
|
|
|
|
cachedDisassembly =
|
|
new std::string(generateDisassembly(pc, symtab));
|
|
cachedPC = pc;
|
|
cachedSymtab = symtab;
|
|
}
|
|
|
|
return *cachedDisassembly;
|
|
}
|
|
|
|
std::string
|
|
Branch::generateDisassembly(Addr pc, const SymbolTable *symtab)
|
|
{
|
|
std::stringstream ss;
|
|
|
|
ccprintf(ss, "%-10s ", mnemonic);
|
|
|
|
// There's only one register arg (RA), but it could be
|
|
// either a source (the condition for conditional
|
|
// branches) or a destination (the link reg for
|
|
// unconditional branches)
|
|
if (_numSrcRegs > 0) {
|
|
printReg(ss, _srcRegIdx[0]);
|
|
ss << ",";
|
|
}
|
|
else if (_numDestRegs > 0) {
|
|
printReg(ss, _destRegIdx[0]);
|
|
ss << ",";
|
|
}
|
|
|
|
#ifdef SS_COMPATIBLE_DISASSEMBLY
|
|
if (_numSrcRegs == 0 && _numDestRegs == 0) {
|
|
printReg(ss, 31);
|
|
ss << ",";
|
|
}
|
|
#endif
|
|
|
|
Addr target = pc + 4 + disp;
|
|
|
|
std::string str;
|
|
if (symtab && symtab->findSymbol(target, str))
|
|
ss << str;
|
|
else
|
|
ccprintf(ss, "0x%x", target);
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
std::string
|
|
Jump::generateDisassembly(Addr pc, const SymbolTable *symtab)
|
|
{
|
|
std::stringstream ss;
|
|
|
|
ccprintf(ss, "%-10s ", mnemonic);
|
|
|
|
#ifdef SS_COMPATIBLE_DISASSEMBLY
|
|
if (_numDestRegs == 0) {
|
|
printReg(ss, 31);
|
|
ss << ",";
|
|
}
|
|
#endif
|
|
|
|
if (_numDestRegs > 0) {
|
|
printReg(ss, _destRegIdx[0]);
|
|
ss << ",";
|
|
}
|
|
|
|
ccprintf(ss, "(r%d)", RB);
|
|
|
|
return ss.str();
|
|
}
|
|
}};
|
|
|
|
def template JumpOrBranchDecode {{
|
|
return (RA == 31)
|
|
? (StaticInst<AlphaISA> *)new %(class_name)s(machInst)
|
|
: (StaticInst<AlphaISA> *)new %(class_name)sAndLink(machInst);
|
|
}};
|
|
|
|
def format CondBranch(code) {{
|
|
code = 'bool cond;\n' + code + '\nif (cond) NPC = NPC + disp;\n';
|
|
iop = InstObjParams(name, Name, 'Branch', CodeBlock(code),
|
|
('IsDirectControl', 'IsCondControl'))
|
|
header_output = BasicDeclare.subst(iop)
|
|
decoder_output = BasicConstructor.subst(iop)
|
|
decode_block = BasicDecode.subst(iop)
|
|
exec_output = BasicExecute.subst(iop)
|
|
}};
|
|
|
|
let {{
|
|
def UncondCtrlBase(name, Name, base_class, npc_expr, flags):
|
|
# Declare basic control transfer w/o link (i.e. link reg is R31)
|
|
nolink_code = 'NPC = %s;\n' % npc_expr
|
|
nolink_iop = InstObjParams(name, Name, base_class,
|
|
CodeBlock(nolink_code), flags)
|
|
header_output = BasicDeclare.subst(nolink_iop)
|
|
decoder_output = BasicConstructor.subst(nolink_iop)
|
|
exec_output = BasicExecute.subst(nolink_iop)
|
|
|
|
# Generate declaration of '*AndLink' version, append to decls
|
|
link_code = 'Ra = NPC & ~3;\n' + nolink_code
|
|
link_iop = InstObjParams(name, Name + 'AndLink', base_class,
|
|
CodeBlock(link_code), flags)
|
|
header_output += BasicDeclare.subst(link_iop)
|
|
decoder_output += BasicConstructor.subst(link_iop)
|
|
exec_output += BasicExecute.subst(link_iop)
|
|
|
|
# need to use link_iop for the decode template since it is expecting
|
|
# the shorter version of class_name (w/o "AndLink")
|
|
|
|
return (header_output, decoder_output,
|
|
JumpOrBranchDecode.subst(nolink_iop), exec_output)
|
|
}};
|
|
|
|
def format UncondBranch(*flags) {{
|
|
flags += ('IsUncondControl', 'IsDirectControl')
|
|
(header_output, decoder_output, decode_block, exec_output) = \
|
|
UncondCtrlBase(name, Name, 'Branch', 'NPC + disp', flags)
|
|
}};
|
|
|
|
def format Jump(*flags) {{
|
|
flags += ('IsUncondControl', 'IsIndirectControl')
|
|
(header_output, decoder_output, decode_block, exec_output) = \
|
|
UncondCtrlBase(name, Name, 'Jump', '(Rb & ~3) | (NPC & 1)', flags)
|
|
}};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PAL calls
|
|
//
|
|
|
|
output header {{
|
|
/**
|
|
* Base class for emulated call_pal calls (used only in
|
|
* non-full-system mode).
|
|
*/
|
|
class EmulatedCallPal : public AlphaStaticInst
|
|
{
|
|
protected:
|
|
|
|
/// Constructor.
|
|
EmulatedCallPal(const char *mnem, MachInst _machInst,
|
|
OpClass __opClass)
|
|
: AlphaStaticInst(mnem, _machInst, __opClass)
|
|
{
|
|
}
|
|
|
|
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
|
|
};
|
|
}};
|
|
|
|
output decoder {{
|
|
std::string
|
|
EmulatedCallPal::generateDisassembly(Addr pc, const SymbolTable *symtab)
|
|
{
|
|
#ifdef SS_COMPATIBLE_DISASSEMBLY
|
|
return csprintf("%s %s", "call_pal", mnemonic);
|
|
#else
|
|
return csprintf("%-10s %s", "call_pal", mnemonic);
|
|
#endif
|
|
}
|
|
}};
|
|
|
|
def format EmulatedCallPal(code, *flags) {{
|
|
iop = InstObjParams(name, Name, 'EmulatedCallPal', CodeBlock(code), flags)
|
|
header_output = BasicDeclare.subst(iop)
|
|
decoder_output = BasicConstructor.subst(iop)
|
|
decode_block = BasicDecode.subst(iop)
|
|
exec_output = BasicExecute.subst(iop)
|
|
}};
|
|
|
|
output header {{
|
|
/**
|
|
* Base class for full-system-mode call_pal instructions.
|
|
* Probably could turn this into a leaf class and get rid of the
|
|
* parser template.
|
|
*/
|
|
class CallPalBase : public AlphaStaticInst
|
|
{
|
|
protected:
|
|
int palFunc; ///< Function code part of instruction
|
|
int palOffset; ///< Target PC, offset from IPR_PAL_BASE
|
|
bool palValid; ///< is the function code valid?
|
|
bool palPriv; ///< is this call privileged?
|
|
|
|
/// Constructor.
|
|
CallPalBase(const char *mnem, MachInst _machInst,
|
|
OpClass __opClass);
|
|
|
|
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
|
|
};
|
|
}};
|
|
|
|
output decoder {{
|
|
inline
|
|
CallPalBase::CallPalBase(const char *mnem, MachInst _machInst,
|
|
OpClass __opClass)
|
|
: AlphaStaticInst(mnem, _machInst, __opClass),
|
|
palFunc(PALFUNC)
|
|
{
|
|
// From the 21164 HRM (paraphrased):
|
|
// Bit 7 of the function code (mask 0x80) indicates
|
|
// whether the call is privileged (bit 7 == 0) or
|
|
// unprivileged (bit 7 == 1). The privileged call table
|
|
// starts at 0x2000, the unprivielged call table starts at
|
|
// 0x3000. Bits 5-0 (mask 0x3f) are used to calculate the
|
|
// offset.
|
|
const int palPrivMask = 0x80;
|
|
const int palOffsetMask = 0x3f;
|
|
|
|
// Pal call is invalid unless all other bits are 0
|
|
palValid = ((machInst & ~(palPrivMask | palOffsetMask)) == 0);
|
|
palPriv = ((machInst & palPrivMask) == 0);
|
|
int shortPalFunc = (machInst & palOffsetMask);
|
|
// Add 1 to base to set pal-mode bit
|
|
palOffset = (palPriv ? 0x2001 : 0x3001) + (shortPalFunc << 6);
|
|
}
|
|
|
|
std::string
|
|
CallPalBase::generateDisassembly(Addr pc, const SymbolTable *symtab)
|
|
{
|
|
return csprintf("%-10s %#x", "call_pal", palFunc);
|
|
}
|
|
}};
|
|
|
|
def format CallPal(code, *flags) {{
|
|
iop = InstObjParams(name, Name, 'CallPalBase', CodeBlock(code), flags)
|
|
header_output = BasicDeclare.subst(iop)
|
|
decoder_output = BasicConstructor.subst(iop)
|
|
decode_block = BasicDecode.subst(iop)
|
|
exec_output = BasicExecute.subst(iop)
|
|
}};
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// hw_ld, hw_st
|
|
//
|
|
|
|
output header {{
|
|
/**
|
|
* Base class for hw_ld and hw_st.
|
|
*/
|
|
class HwLoadStore : public Memory
|
|
{
|
|
protected:
|
|
|
|
/// Displacement for EA calculation (signed).
|
|
int16_t disp;
|
|
|
|
/// Constructor
|
|
HwLoadStore(const char *mnem, MachInst _machInst, OpClass __opClass,
|
|
StaticInstPtr<AlphaISA> _eaCompPtr,
|
|
StaticInstPtr<AlphaISA> _memAccPtr);
|
|
|
|
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
|
|
};
|
|
}};
|
|
|
|
|
|
output decoder {{
|
|
inline
|
|
HwLoadStore::HwLoadStore(const char *mnem, MachInst _machInst,
|
|
OpClass __opClass,
|
|
StaticInstPtr<AlphaISA> _eaCompPtr,
|
|
StaticInstPtr<AlphaISA> _memAccPtr)
|
|
: Memory(mnem, _machInst, __opClass, _eaCompPtr, _memAccPtr),
|
|
disp(HW_LDST_DISP)
|
|
{
|
|
memAccessFlags = 0;
|
|
if (HW_LDST_PHYS) memAccessFlags |= PHYSICAL;
|
|
if (HW_LDST_ALT) memAccessFlags |= ALTMODE;
|
|
if (HW_LDST_VPTE) memAccessFlags |= VPTE;
|
|
if (HW_LDST_LOCK) memAccessFlags |= LOCKED;
|
|
}
|
|
|
|
std::string
|
|
HwLoadStore::generateDisassembly(Addr pc, const SymbolTable *symtab)
|
|
{
|
|
#ifdef SS_COMPATIBLE_DISASSEMBLY
|
|
return csprintf("%-10s r%d,%d(r%d)", mnemonic, RA, disp, RB);
|
|
#else
|
|
// HW_LDST_LOCK and HW_LDST_COND are the same bit.
|
|
const char *lock_str =
|
|
(HW_LDST_LOCK) ? (flags[IsLoad] ? ",LOCK" : ",COND") : "";
|
|
|
|
return csprintf("%-10s r%d,%d(r%d)%s%s%s%s%s",
|
|
mnemonic, RA, disp, RB,
|
|
HW_LDST_PHYS ? ",PHYS" : "",
|
|
HW_LDST_ALT ? ",ALT" : "",
|
|
HW_LDST_QUAD ? ",QUAD" : "",
|
|
HW_LDST_VPTE ? ",VPTE" : "",
|
|
lock_str);
|
|
#endif
|
|
}
|
|
}};
|
|
|
|
def format HwLoadStore(ea_code, memacc_code, class_ext, *flags) {{
|
|
(header_output, decoder_output, decode_block, exec_output) = \
|
|
LoadStoreBase(name, Name + class_ext, ea_code, memacc_code,
|
|
flags = flags, base_class = 'HwLoadStore')
|
|
}};
|
|
|
|
|
|
def format HwStoreCond(ea_code, memacc_code, postacc_code, class_ext, *flags) {{
|
|
(header_output, decoder_output, decode_block, exec_output) = \
|
|
LoadStoreBase(name, Name + class_ext, ea_code, memacc_code,
|
|
postacc_code, flags = flags, base_class = 'HwLoadStore')
|
|
}};
|
|
|
|
|
|
output header {{
|
|
/**
|
|
* Base class for hw_mfpr and hw_mtpr.
|
|
*/
|
|
class HwMoveIPR : public AlphaStaticInst
|
|
{
|
|
protected:
|
|
/// Index of internal processor register.
|
|
int ipr_index;
|
|
|
|
/// Constructor
|
|
HwMoveIPR(const char *mnem, MachInst _machInst, OpClass __opClass)
|
|
: AlphaStaticInst(mnem, _machInst, __opClass),
|
|
ipr_index(HW_IPR_IDX)
|
|
{
|
|
}
|
|
|
|
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
|
|
};
|
|
}};
|
|
|
|
output decoder {{
|
|
std::string
|
|
HwMoveIPR::generateDisassembly(Addr pc, const SymbolTable *symtab)
|
|
{
|
|
if (_numSrcRegs > 0) {
|
|
// must be mtpr
|
|
return csprintf("%-10s r%d,IPR(%#x)",
|
|
mnemonic, RA, ipr_index);
|
|
}
|
|
else {
|
|
// must be mfpr
|
|
return csprintf("%-10s IPR(%#x),r%d",
|
|
mnemonic, ipr_index, RA);
|
|
}
|
|
}
|
|
}};
|
|
|
|
def format HwMoveIPR(code) {{
|
|
iop = InstObjParams(name, Name, 'HwMoveIPR', CodeBlock(code))
|
|
header_output = BasicDeclare.subst(iop)
|
|
decoder_output = BasicConstructor.subst(iop)
|
|
decode_block = BasicDecode.subst(iop)
|
|
exec_output = BasicExecute.subst(iop)
|
|
}};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Unimplemented instructions
|
|
//
|
|
|
|
output header {{
|
|
/**
|
|
* Static instruction class for unimplemented instructions that
|
|
* cause simulator termination. Note that these are recognized
|
|
* (legal) instructions that the simulator does not support; the
|
|
* 'Unknown' class is used for unrecognized/illegal instructions.
|
|
* This is a leaf class.
|
|
*/
|
|
class FailUnimplemented : public AlphaStaticInst
|
|
{
|
|
public:
|
|
/// Constructor
|
|
FailUnimplemented(const char *_mnemonic, MachInst _machInst)
|
|
: AlphaStaticInst(_mnemonic, _machInst, No_OpClass)
|
|
{
|
|
// don't call execute() (which panics) if we're on a
|
|
// speculative path
|
|
flags[IsNonSpeculative] = true;
|
|
}
|
|
|
|
%(BasicExecDeclare)s
|
|
|
|
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
|
|
};
|
|
|
|
/**
|
|
* Base class for unimplemented instructions that cause a warning
|
|
* to be printed (but do not terminate simulation). This
|
|
* implementation is a little screwy in that it will print a
|
|
* warning for each instance of a particular unimplemented machine
|
|
* instruction, not just for each unimplemented opcode. Should
|
|
* probably make the 'warned' flag a static member of the derived
|
|
* class.
|
|
*/
|
|
class WarnUnimplemented : public AlphaStaticInst
|
|
{
|
|
private:
|
|
/// Have we warned on this instruction yet?
|
|
bool warned;
|
|
|
|
public:
|
|
/// Constructor
|
|
WarnUnimplemented(const char *_mnemonic, MachInst _machInst)
|
|
: AlphaStaticInst(_mnemonic, _machInst, No_OpClass), warned(false)
|
|
{
|
|
// don't call execute() (which panics) if we're on a
|
|
// speculative path
|
|
flags[IsNonSpeculative] = true;
|
|
}
|
|
|
|
%(BasicExecDeclare)s
|
|
|
|
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
|
|
};
|
|
}};
|
|
|
|
output decoder {{
|
|
std::string
|
|
FailUnimplemented::generateDisassembly(Addr pc, const SymbolTable *symtab)
|
|
{
|
|
return csprintf("%-10s (unimplemented)", mnemonic);
|
|
}
|
|
|
|
std::string
|
|
WarnUnimplemented::generateDisassembly(Addr pc, const SymbolTable *symtab)
|
|
{
|
|
#ifdef SS_COMPATIBLE_DISASSEMBLY
|
|
return csprintf("%-10s", mnemonic);
|
|
#else
|
|
return csprintf("%-10s (unimplemented)", mnemonic);
|
|
#endif
|
|
}
|
|
}};
|
|
|
|
output exec {{
|
|
Fault
|
|
FailUnimplemented::execute(%(CPU_exec_context)s *xc,
|
|
Trace::InstRecord *traceData)
|
|
{
|
|
panic("attempt to execute unimplemented instruction '%s' "
|
|
"(inst 0x%08x, opcode 0x%x)", mnemonic, machInst, OPCODE);
|
|
return Unimplemented_Opcode_Fault;
|
|
}
|
|
|
|
Fault
|
|
WarnUnimplemented::execute(%(CPU_exec_context)s *xc,
|
|
Trace::InstRecord *traceData)
|
|
{
|
|
if (!warned) {
|
|
warn("instruction '%s' unimplemented\n", mnemonic);
|
|
warned = true;
|
|
}
|
|
|
|
return No_Fault;
|
|
}
|
|
}};
|
|
|
|
|
|
def format FailUnimpl() {{
|
|
iop = InstObjParams(name, 'FailUnimplemented')
|
|
decode_block = BasicDecodeWithMnemonic.subst(iop)
|
|
}};
|
|
|
|
def format WarnUnimpl() {{
|
|
iop = InstObjParams(name, 'WarnUnimplemented')
|
|
decode_block = BasicDecodeWithMnemonic.subst(iop)
|
|
}};
|
|
|
|
output header {{
|
|
/**
|
|
* Static instruction class for unknown (illegal) instructions.
|
|
* These cause simulator termination if they are executed in a
|
|
* non-speculative mode. This is a leaf class.
|
|
*/
|
|
class Unknown : public AlphaStaticInst
|
|
{
|
|
public:
|
|
/// Constructor
|
|
Unknown(MachInst _machInst)
|
|
: AlphaStaticInst("unknown", _machInst, No_OpClass)
|
|
{
|
|
// don't call execute() (which panics) if we're on a
|
|
// speculative path
|
|
flags[IsNonSpeculative] = true;
|
|
}
|
|
|
|
%(BasicExecDeclare)s
|
|
|
|
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
|
|
};
|
|
}};
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Unknown instructions
|
|
//
|
|
|
|
output decoder {{
|
|
std::string
|
|
Unknown::generateDisassembly(Addr pc, const SymbolTable *symtab)
|
|
{
|
|
return csprintf("%-10s (inst 0x%x, opcode 0x%x)",
|
|
"unknown", machInst, OPCODE);
|
|
}
|
|
}};
|
|
|
|
output exec {{
|
|
Fault
|
|
Unknown::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData)
|
|
{
|
|
panic("attempt to execute unknown instruction "
|
|
"(inst 0x%08x, opcode 0x%x)", machInst, OPCODE);
|
|
return Unimplemented_Opcode_Fault;
|
|
}
|
|
}};
|
|
|
|
def format Unknown() {{
|
|
decode_block = 'return new Unknown(machInst);\n'
|
|
}};
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Utility functions for execute methods
|
|
//
|
|
|
|
output exec {{
|
|
|
|
/// Return opa + opb, summing carry into third arg.
|
|
inline uint64_t
|
|
addc(uint64_t opa, uint64_t opb, int &carry)
|
|
{
|
|
uint64_t res = opa + opb;
|
|
if (res < opa || res < opb)
|
|
++carry;
|
|
return res;
|
|
}
|
|
|
|
/// Multiply two 64-bit values (opa * opb), returning the 128-bit
|
|
/// product in res_hi and res_lo.
|
|
inline void
|
|
mul128(uint64_t opa, uint64_t opb, uint64_t &res_hi, uint64_t &res_lo)
|
|
{
|
|
// do a 64x64 --> 128 multiply using four 32x32 --> 64 multiplies
|
|
uint64_t opa_hi = opa<63:32>;
|
|
uint64_t opa_lo = opa<31:0>;
|
|
uint64_t opb_hi = opb<63:32>;
|
|
uint64_t opb_lo = opb<31:0>;
|
|
|
|
res_lo = opa_lo * opb_lo;
|
|
|
|
// The middle partial products logically belong in bit
|
|
// positions 95 to 32. Thus the lower 32 bits of each product
|
|
// sum into the upper 32 bits of the low result, while the
|
|
// upper 32 sum into the low 32 bits of the upper result.
|
|
uint64_t partial1 = opa_hi * opb_lo;
|
|
uint64_t partial2 = opa_lo * opb_hi;
|
|
|
|
uint64_t partial1_lo = partial1<31:0> << 32;
|
|
uint64_t partial1_hi = partial1<63:32>;
|
|
uint64_t partial2_lo = partial2<31:0> << 32;
|
|
uint64_t partial2_hi = partial2<63:32>;
|
|
|
|
// Add partial1_lo and partial2_lo to res_lo, keeping track
|
|
// of any carries out
|
|
int carry_out = 0;
|
|
res_lo = addc(partial1_lo, res_lo, carry_out);
|
|
res_lo = addc(partial2_lo, res_lo, carry_out);
|
|
|
|
// Now calculate the high 64 bits...
|
|
res_hi = (opa_hi * opb_hi) + partial1_hi + partial2_hi + carry_out;
|
|
}
|
|
|
|
/// Map 8-bit S-floating exponent to 11-bit T-floating exponent.
|
|
/// See Table 2-2 of Alpha AHB.
|
|
inline int
|
|
map_s(int old_exp)
|
|
{
|
|
int hibit = old_exp<7:>;
|
|
int lobits = old_exp<6:0>;
|
|
|
|
if (hibit == 1) {
|
|
return (lobits == 0x7f) ? 0x7ff : (0x400 | lobits);
|
|
}
|
|
else {
|
|
return (lobits == 0) ? 0 : (0x380 | lobits);
|
|
}
|
|
}
|
|
|
|
/// Convert a 32-bit S-floating value to the equivalent 64-bit
|
|
/// representation to be stored in an FP reg.
|
|
inline uint64_t
|
|
s_to_t(uint32_t s_val)
|
|
{
|
|
uint64_t tmp = s_val;
|
|
return (tmp<31:> << 63 // sign bit
|
|
| (uint64_t)map_s(tmp<30:23>) << 52 // exponent
|
|
| tmp<22:0> << 29); // fraction
|
|
}
|
|
|
|
/// Convert a 64-bit T-floating value to the equivalent 32-bit
|
|
/// S-floating representation to be stored in memory.
|
|
inline int32_t
|
|
t_to_s(uint64_t t_val)
|
|
{
|
|
return (t_val<63:62> << 30 // sign bit & hi exp bit
|
|
| t_val<58:29>); // rest of exp & fraction
|
|
}
|
|
}};
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// The actual decoder specification
|
|
//
|
|
|
|
decode OPCODE default Unknown::unknown() {
|
|
|
|
format LoadAddress {
|
|
0x08: lda({{ Ra = Rb + disp; }});
|
|
0x09: ldah({{ Ra = Rb + (disp << 16); }});
|
|
}
|
|
|
|
format LoadOrNop {
|
|
0x0a: ldbu({{ EA = Rb + disp; }}, {{ Ra.uq = Mem.ub; }});
|
|
0x0c: ldwu({{ EA = Rb + disp; }}, {{ Ra.uq = Mem.uw; }});
|
|
0x0b: ldq_u({{ EA = (Rb + disp) & ~7; }}, {{ Ra = Mem.uq; }});
|
|
0x23: ldt({{ EA = Rb + disp; }}, {{ Fa = Mem.df; }});
|
|
0x2a: ldl_l({{ EA = Rb + disp; }}, {{ Ra.sl = Mem.sl; }}, LOCKED);
|
|
0x2b: ldq_l({{ EA = Rb + disp; }}, {{ Ra.uq = Mem.uq; }}, LOCKED);
|
|
0x20: copy_load({{EA = Ra;}},
|
|
{{fault = xc->copySrcTranslate(EA);}},
|
|
IsMemRef, IsLoad, IsCopy);
|
|
}
|
|
|
|
format LoadOrPrefetch {
|
|
0x28: ldl({{ EA = Rb + disp; }}, {{ Ra.sl = Mem.sl; }});
|
|
0x29: ldq({{ EA = Rb + disp; }}, {{ Ra.uq = Mem.uq; }}, EVICT_NEXT);
|
|
// IsFloating flag on lds gets the prefetch to disassemble
|
|
// using f31 instead of r31... funcitonally it's unnecessary
|
|
0x22: lds({{ EA = Rb + disp; }}, {{ Fa.uq = s_to_t(Mem.ul); }},
|
|
PF_EXCLUSIVE, IsFloating);
|
|
}
|
|
|
|
format Store {
|
|
0x0e: stb({{ EA = Rb + disp; }}, {{ Mem.ub = Ra<7:0>; }});
|
|
0x0d: stw({{ EA = Rb + disp; }}, {{ Mem.uw = Ra<15:0>; }});
|
|
0x2c: stl({{ EA = Rb + disp; }}, {{ Mem.ul = Ra<31:0>; }});
|
|
0x2d: stq({{ EA = Rb + disp; }}, {{ Mem.uq = Ra.uq; }});
|
|
0x0f: stq_u({{ EA = (Rb + disp) & ~7; }}, {{ Mem.uq = Ra.uq; }});
|
|
0x26: sts({{ EA = Rb + disp; }}, {{ Mem.ul = t_to_s(Fa.uq); }});
|
|
0x27: stt({{ EA = Rb + disp; }}, {{ Mem.df = Fa; }});
|
|
0x24: copy_store({{EA = Rb;}},
|
|
{{fault = xc->copy(EA);}},
|
|
IsMemRef, IsStore, IsCopy);
|
|
}
|
|
|
|
format StoreCond {
|
|
0x2e: stl_c({{ EA = Rb + disp; }}, {{ Mem.ul = Ra<31:0>; }},
|
|
{{
|
|
uint64_t tmp = Mem_write_result;
|
|
// see stq_c
|
|
Ra = (tmp == 0 || tmp == 1) ? tmp : Ra;
|
|
}}, LOCKED);
|
|
0x2f: stq_c({{ EA = Rb + disp; }}, {{ Mem.uq = Ra; }},
|
|
{{
|
|
uint64_t tmp = Mem_write_result;
|
|
// If the write operation returns 0 or 1, then
|
|
// this was a conventional store conditional,
|
|
// and the value indicates the success/failure
|
|
// of the operation. If another value is
|
|
// returned, then this was a Turbolaser
|
|
// mailbox access, and we don't update the
|
|
// result register at all.
|
|
Ra = (tmp == 0 || tmp == 1) ? tmp : Ra;
|
|
}}, LOCKED);
|
|
}
|
|
|
|
format IntegerOperate {
|
|
|
|
0x10: decode INTFUNC { // integer arithmetic operations
|
|
|
|
0x00: addl({{ Rc.sl = Ra.sl + Rb_or_imm.sl; }});
|
|
0x40: addlv({{
|
|
uint32_t tmp = Ra.sl + Rb_or_imm.sl;
|
|
// signed overflow occurs when operands have same sign
|
|
// and sign of result does not match.
|
|
if (Ra.sl<31:> == Rb_or_imm.sl<31:> && tmp<31:> != Ra.sl<31:>)
|
|
fault = Integer_Overflow_Fault;
|
|
Rc.sl = tmp;
|
|
}});
|
|
0x02: s4addl({{ Rc.sl = (Ra.sl << 2) + Rb_or_imm.sl; }});
|
|
0x12: s8addl({{ Rc.sl = (Ra.sl << 3) + Rb_or_imm.sl; }});
|
|
|
|
0x20: addq({{ Rc = Ra + Rb_or_imm; }});
|
|
0x60: addqv({{
|
|
uint64_t tmp = Ra + Rb_or_imm;
|
|
// signed overflow occurs when operands have same sign
|
|
// and sign of result does not match.
|
|
if (Ra<63:> == Rb_or_imm<63:> && tmp<63:> != Ra<63:>)
|
|
fault = Integer_Overflow_Fault;
|
|
Rc = tmp;
|
|
}});
|
|
0x22: s4addq({{ Rc = (Ra << 2) + Rb_or_imm; }});
|
|
0x32: s8addq({{ Rc = (Ra << 3) + Rb_or_imm; }});
|
|
|
|
0x09: subl({{ Rc.sl = Ra.sl - Rb_or_imm.sl; }});
|
|
0x49: sublv({{
|
|
uint32_t tmp = Ra.sl - Rb_or_imm.sl;
|
|
// signed overflow detection is same as for add,
|
|
// except we need to look at the *complemented*
|
|
// sign bit of the subtrahend (Rb), i.e., if the initial
|
|
// signs are the *same* then no overflow can occur
|
|
if (Ra.sl<31:> != Rb_or_imm.sl<31:> && tmp<31:> != Ra.sl<31:>)
|
|
fault = Integer_Overflow_Fault;
|
|
Rc.sl = tmp;
|
|
}});
|
|
0x0b: s4subl({{ Rc.sl = (Ra.sl << 2) - Rb_or_imm.sl; }});
|
|
0x1b: s8subl({{ Rc.sl = (Ra.sl << 3) - Rb_or_imm.sl; }});
|
|
|
|
0x29: subq({{ Rc = Ra - Rb_or_imm; }});
|
|
0x69: subqv({{
|
|
uint64_t tmp = Ra - Rb_or_imm;
|
|
// signed overflow detection is same as for add,
|
|
// except we need to look at the *complemented*
|
|
// sign bit of the subtrahend (Rb), i.e., if the initial
|
|
// signs are the *same* then no overflow can occur
|
|
if (Ra<63:> != Rb_or_imm<63:> && tmp<63:> != Ra<63:>)
|
|
fault = Integer_Overflow_Fault;
|
|
Rc = tmp;
|
|
}});
|
|
0x2b: s4subq({{ Rc = (Ra << 2) - Rb_or_imm; }});
|
|
0x3b: s8subq({{ Rc = (Ra << 3) - Rb_or_imm; }});
|
|
|
|
0x2d: cmpeq({{ Rc = (Ra == Rb_or_imm); }});
|
|
0x6d: cmple({{ Rc = (Ra.sq <= Rb_or_imm.sq); }});
|
|
0x4d: cmplt({{ Rc = (Ra.sq < Rb_or_imm.sq); }});
|
|
0x3d: cmpule({{ Rc = (Ra.uq <= Rb_or_imm.uq); }});
|
|
0x1d: cmpult({{ Rc = (Ra.uq < Rb_or_imm.uq); }});
|
|
|
|
0x0f: cmpbge({{
|
|
int hi = 7;
|
|
int lo = 0;
|
|
uint64_t tmp = 0;
|
|
for (int i = 0; i < 8; ++i) {
|
|
tmp |= (Ra.uq<hi:lo> >= Rb_or_imm.uq<hi:lo>) << i;
|
|
hi += 8;
|
|
lo += 8;
|
|
}
|
|
Rc = tmp;
|
|
}});
|
|
}
|
|
|
|
0x11: decode INTFUNC { // integer logical operations
|
|
|
|
0x00: and({{ Rc = Ra & Rb_or_imm; }});
|
|
0x08: bic({{ Rc = Ra & ~Rb_or_imm; }});
|
|
0x20: bis({{ Rc = Ra | Rb_or_imm; }});
|
|
0x28: ornot({{ Rc = Ra | ~Rb_or_imm; }});
|
|
0x40: xor({{ Rc = Ra ^ Rb_or_imm; }});
|
|
0x48: eqv({{ Rc = Ra ^ ~Rb_or_imm; }});
|
|
|
|
// conditional moves
|
|
0x14: cmovlbs({{ Rc = ((Ra & 1) == 1) ? Rb_or_imm : Rc; }});
|
|
0x16: cmovlbc({{ Rc = ((Ra & 1) == 0) ? Rb_or_imm : Rc; }});
|
|
0x24: cmoveq({{ Rc = (Ra == 0) ? Rb_or_imm : Rc; }});
|
|
0x26: cmovne({{ Rc = (Ra != 0) ? Rb_or_imm : Rc; }});
|
|
0x44: cmovlt({{ Rc = (Ra.sq < 0) ? Rb_or_imm : Rc; }});
|
|
0x46: cmovge({{ Rc = (Ra.sq >= 0) ? Rb_or_imm : Rc; }});
|
|
0x64: cmovle({{ Rc = (Ra.sq <= 0) ? Rb_or_imm : Rc; }});
|
|
0x66: cmovgt({{ Rc = (Ra.sq > 0) ? Rb_or_imm : Rc; }});
|
|
|
|
// For AMASK, RA must be R31.
|
|
0x61: decode RA {
|
|
31: amask({{ Rc = Rb_or_imm & ~ULL(0x17); }});
|
|
}
|
|
|
|
// For IMPLVER, RA must be R31 and the B operand
|
|
// must be the immediate value 1.
|
|
0x6c: decode RA {
|
|
31: decode IMM {
|
|
1: decode INTIMM {
|
|
// return EV5 for FULL_SYSTEM and EV6 otherwise
|
|
1: implver({{
|
|
#ifdef FULL_SYSTEM
|
|
Rc = 1;
|
|
#else
|
|
Rc = 2;
|
|
#endif
|
|
}});
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef FULL_SYSTEM
|
|
// The mysterious 11.25...
|
|
0x25: WarnUnimpl::eleven25();
|
|
#endif
|
|
}
|
|
|
|
0x12: decode INTFUNC {
|
|
0x39: sll({{ Rc = Ra << Rb_or_imm<5:0>; }});
|
|
0x34: srl({{ Rc = Ra.uq >> Rb_or_imm<5:0>; }});
|
|
0x3c: sra({{ Rc = Ra.sq >> Rb_or_imm<5:0>; }});
|
|
|
|
0x02: mskbl({{ Rc = Ra & ~(mask( 8) << (Rb_or_imm<2:0> * 8)); }});
|
|
0x12: mskwl({{ Rc = Ra & ~(mask(16) << (Rb_or_imm<2:0> * 8)); }});
|
|
0x22: mskll({{ Rc = Ra & ~(mask(32) << (Rb_or_imm<2:0> * 8)); }});
|
|
0x32: mskql({{ Rc = Ra & ~(mask(64) << (Rb_or_imm<2:0> * 8)); }});
|
|
|
|
0x52: mskwh({{
|
|
int bv = Rb_or_imm<2:0>;
|
|
Rc = bv ? (Ra & ~(mask(16) >> (64 - 8 * bv))) : Ra;
|
|
}});
|
|
0x62: msklh({{
|
|
int bv = Rb_or_imm<2:0>;
|
|
Rc = bv ? (Ra & ~(mask(32) >> (64 - 8 * bv))) : Ra;
|
|
}});
|
|
0x72: mskqh({{
|
|
int bv = Rb_or_imm<2:0>;
|
|
Rc = bv ? (Ra & ~(mask(64) >> (64 - 8 * bv))) : Ra;
|
|
}});
|
|
|
|
0x06: extbl({{ Rc = (Ra.uq >> (Rb_or_imm<2:0> * 8))< 7:0>; }});
|
|
0x16: extwl({{ Rc = (Ra.uq >> (Rb_or_imm<2:0> * 8))<15:0>; }});
|
|
0x26: extll({{ Rc = (Ra.uq >> (Rb_or_imm<2:0> * 8))<31:0>; }});
|
|
0x36: extql({{ Rc = (Ra.uq >> (Rb_or_imm<2:0> * 8)); }});
|
|
|
|
0x5a: extwh({{
|
|
Rc = (Ra << (64 - (Rb_or_imm<2:0> * 8))<5:0>)<15:0>; }});
|
|
0x6a: extlh({{
|
|
Rc = (Ra << (64 - (Rb_or_imm<2:0> * 8))<5:0>)<31:0>; }});
|
|
0x7a: extqh({{
|
|
Rc = (Ra << (64 - (Rb_or_imm<2:0> * 8))<5:0>); }});
|
|
|
|
0x0b: insbl({{ Rc = Ra< 7:0> << (Rb_or_imm<2:0> * 8); }});
|
|
0x1b: inswl({{ Rc = Ra<15:0> << (Rb_or_imm<2:0> * 8); }});
|
|
0x2b: insll({{ Rc = Ra<31:0> << (Rb_or_imm<2:0> * 8); }});
|
|
0x3b: insql({{ Rc = Ra << (Rb_or_imm<2:0> * 8); }});
|
|
|
|
0x57: inswh({{
|
|
int bv = Rb_or_imm<2:0>;
|
|
Rc = bv ? (Ra.uq<15:0> >> (64 - 8 * bv)) : 0;
|
|
}});
|
|
0x67: inslh({{
|
|
int bv = Rb_or_imm<2:0>;
|
|
Rc = bv ? (Ra.uq<31:0> >> (64 - 8 * bv)) : 0;
|
|
}});
|
|
0x77: insqh({{
|
|
int bv = Rb_or_imm<2:0>;
|
|
Rc = bv ? (Ra.uq >> (64 - 8 * bv)) : 0;
|
|
}});
|
|
|
|
0x30: zap({{
|
|
uint64_t zapmask = 0;
|
|
for (int i = 0; i < 8; ++i) {
|
|
if (Rb_or_imm<i:>)
|
|
zapmask |= (mask(8) << (i * 8));
|
|
}
|
|
Rc = Ra & ~zapmask;
|
|
}});
|
|
0x31: zapnot({{
|
|
uint64_t zapmask = 0;
|
|
for (int i = 0; i < 8; ++i) {
|
|
if (!Rb_or_imm<i:>)
|
|
zapmask |= (mask(8) << (i * 8));
|
|
}
|
|
Rc = Ra & ~zapmask;
|
|
}});
|
|
}
|
|
|
|
0x13: decode INTFUNC { // integer multiplies
|
|
0x00: mull({{ Rc.sl = Ra.sl * Rb_or_imm.sl; }}, IntMultOp);
|
|
0x20: mulq({{ Rc = Ra * Rb_or_imm; }}, IntMultOp);
|
|
0x30: umulh({{
|
|
uint64_t hi, lo;
|
|
mul128(Ra, Rb_or_imm, hi, lo);
|
|
Rc = hi;
|
|
}}, IntMultOp);
|
|
0x40: mullv({{
|
|
// 32-bit multiply with trap on overflow
|
|
int64_t Rax = Ra.sl; // sign extended version of Ra.sl
|
|
int64_t Rbx = Rb_or_imm.sl;
|
|
int64_t tmp = Rax * Rbx;
|
|
// To avoid overflow, all the upper 32 bits must match
|
|
// the sign bit of the lower 32. We code this as
|
|
// checking the upper 33 bits for all 0s or all 1s.
|
|
uint64_t sign_bits = tmp<63:31>;
|
|
if (sign_bits != 0 && sign_bits != mask(33))
|
|
fault = Integer_Overflow_Fault;
|
|
Rc.sl = tmp<31:0>;
|
|
}}, IntMultOp);
|
|
0x60: mulqv({{
|
|
// 64-bit multiply with trap on overflow
|
|
uint64_t hi, lo;
|
|
mul128(Ra, Rb_or_imm, hi, lo);
|
|
// all the upper 64 bits must match the sign bit of
|
|
// the lower 64
|
|
if (!((hi == 0 && lo<63:> == 0) ||
|
|
(hi == mask(64) && lo<63:> == 1)))
|
|
fault = Integer_Overflow_Fault;
|
|
Rc = lo;
|
|
}}, IntMultOp);
|
|
}
|
|
|
|
0x1c: decode INTFUNC {
|
|
0x00: decode RA { 31: sextb({{ Rc.sb = Rb_or_imm< 7:0>; }}); }
|
|
0x01: decode RA { 31: sextw({{ Rc.sw = Rb_or_imm<15:0>; }}); }
|
|
0x32: ctlz({{
|
|
uint64_t count = 0;
|
|
uint64_t temp = Rb;
|
|
if (temp<63:32>) temp >>= 32; else count += 32;
|
|
if (temp<31:16>) temp >>= 16; else count += 16;
|
|
if (temp<15:8>) temp >>= 8; else count += 8;
|
|
if (temp<7:4>) temp >>= 4; else count += 4;
|
|
if (temp<3:2>) temp >>= 2; else count += 2;
|
|
if (temp<1:1>) temp >>= 1; else count += 1;
|
|
if ((temp<0:0>) != 0x1) count += 1;
|
|
Rc = count;
|
|
}}, IntAluOp);
|
|
|
|
0x33: cttz({{
|
|
uint64_t count = 0;
|
|
uint64_t temp = Rb;
|
|
if (!(temp<31:0>)) { temp >>= 32; count += 32; }
|
|
if (!(temp<15:0>)) { temp >>= 16; count += 16; }
|
|
if (!(temp<7:0>)) { temp >>= 8; count += 8; }
|
|
if (!(temp<3:0>)) { temp >>= 4; count += 4; }
|
|
if (!(temp<1:0>)) { temp >>= 2; count += 2; }
|
|
if (!(temp<0:0> & ULL(0x1))) count += 1;
|
|
Rc = count;
|
|
}}, IntAluOp);
|
|
|
|
format FailUnimpl {
|
|
0x30: ctpop();
|
|
0x31: perr();
|
|
0x34: unpkbw();
|
|
0x35: unpkbl();
|
|
0x36: pkwb();
|
|
0x37: pklb();
|
|
0x38: minsb8();
|
|
0x39: minsw4();
|
|
0x3a: minub8();
|
|
0x3b: minuw4();
|
|
0x3c: maxub8();
|
|
0x3d: maxuw4();
|
|
0x3e: maxsb8();
|
|
0x3f: maxsw4();
|
|
}
|
|
|
|
format BasicOperateWithNopCheck {
|
|
0x70: decode RB {
|
|
31: ftoit({{ Rc = Fa.uq; }}, FloatCvtOp);
|
|
}
|
|
0x78: decode RB {
|
|
31: ftois({{ Rc.sl = t_to_s(Fa.uq); }},
|
|
FloatCvtOp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Conditional branches.
|
|
format CondBranch {
|
|
0x39: beq({{ cond = (Ra == 0); }});
|
|
0x3d: bne({{ cond = (Ra != 0); }});
|
|
0x3e: bge({{ cond = (Ra.sq >= 0); }});
|
|
0x3f: bgt({{ cond = (Ra.sq > 0); }});
|
|
0x3b: ble({{ cond = (Ra.sq <= 0); }});
|
|
0x3a: blt({{ cond = (Ra.sq < 0); }});
|
|
0x38: blbc({{ cond = ((Ra & 1) == 0); }});
|
|
0x3c: blbs({{ cond = ((Ra & 1) == 1); }});
|
|
|
|
0x31: fbeq({{ cond = (Fa == 0); }});
|
|
0x35: fbne({{ cond = (Fa != 0); }});
|
|
0x36: fbge({{ cond = (Fa >= 0); }});
|
|
0x37: fbgt({{ cond = (Fa > 0); }});
|
|
0x33: fble({{ cond = (Fa <= 0); }});
|
|
0x32: fblt({{ cond = (Fa < 0); }});
|
|
}
|
|
|
|
// unconditional branches
|
|
format UncondBranch {
|
|
0x30: br();
|
|
0x34: bsr(IsCall);
|
|
}
|
|
|
|
// indirect branches
|
|
0x1a: decode JMPFUNC {
|
|
format Jump {
|
|
0: jmp();
|
|
1: jsr(IsCall);
|
|
2: ret(IsReturn);
|
|
3: jsr_coroutine(IsCall, IsReturn);
|
|
}
|
|
}
|
|
|
|
// IEEE floating point
|
|
0x14: decode FP_SHORTFUNC {
|
|
// Integer to FP register moves must have RB == 31
|
|
0x4: decode RB {
|
|
31: decode FP_FULLFUNC {
|
|
format BasicOperateWithNopCheck {
|
|
0x004: itofs({{ Fc.uq = s_to_t(Ra.ul); }}, FloatCvtOp);
|
|
0x024: itoft({{ Fc.uq = Ra.uq; }}, FloatCvtOp);
|
|
0x014: FailUnimpl::itoff(); // VAX-format conversion
|
|
}
|
|
}
|
|
}
|
|
|
|
// Square root instructions must have FA == 31
|
|
0xb: decode FA {
|
|
31: decode FP_TYPEFUNC {
|
|
format FloatingPointOperate {
|
|
#ifdef SS_COMPATIBLE_FP
|
|
0x0b: sqrts({{
|
|
if (Fb < 0.0)
|
|
fault = Arithmetic_Fault;
|
|
Fc = sqrt(Fb);
|
|
}}, FloatSqrtOp);
|
|
#else
|
|
0x0b: sqrts({{
|
|
if (Fb.sf < 0.0)
|
|
fault = Arithmetic_Fault;
|
|
Fc.sf = sqrt(Fb.sf);
|
|
}}, FloatSqrtOp);
|
|
#endif
|
|
0x2b: sqrtt({{
|
|
if (Fb < 0.0)
|
|
fault = Arithmetic_Fault;
|
|
Fc = sqrt(Fb);
|
|
}}, FloatSqrtOp);
|
|
}
|
|
}
|
|
}
|
|
|
|
// VAX-format sqrtf and sqrtg are not implemented
|
|
0xa: FailUnimpl::sqrtfg();
|
|
}
|
|
|
|
// IEEE floating point
|
|
0x16: decode FP_SHORTFUNC_TOP2 {
|
|
// The top two bits of the short function code break this space
|
|
// into four groups: binary ops, compares, reserved, and conversions.
|
|
// See Table 4-12 of AHB.
|
|
// Most of these instructions may have various trapping and
|
|
// rounding mode flags set; these are decoded in the
|
|
// FloatingPointDecode template used by the
|
|
// FloatingPointOperate format.
|
|
|
|
// add/sub/mul/div: just decode on the short function code
|
|
// and source type.
|
|
0: decode FP_TYPEFUNC {
|
|
format FloatingPointOperate {
|
|
#ifdef SS_COMPATIBLE_FP
|
|
0x00: adds({{ Fc = Fa + Fb; }});
|
|
0x01: subs({{ Fc = Fa - Fb; }});
|
|
0x02: muls({{ Fc = Fa * Fb; }}, FloatMultOp);
|
|
0x03: divs({{ Fc = Fa / Fb; }}, FloatDivOp);
|
|
#else
|
|
0x00: adds({{ Fc.sf = Fa.sf + Fb.sf; }});
|
|
0x01: subs({{ Fc.sf = Fa.sf - Fb.sf; }});
|
|
0x02: muls({{ Fc.sf = Fa.sf * Fb.sf; }}, FloatMultOp);
|
|
0x03: divs({{ Fc.sf = Fa.sf / Fb.sf; }}, FloatDivOp);
|
|
#endif
|
|
|
|
0x20: addt({{ Fc = Fa + Fb; }});
|
|
0x21: subt({{ Fc = Fa - Fb; }});
|
|
0x22: mult({{ Fc = Fa * Fb; }}, FloatMultOp);
|
|
0x23: divt({{ Fc = Fa / Fb; }}, FloatDivOp);
|
|
}
|
|
}
|
|
|
|
// Floating-point compare instructions must have the default
|
|
// rounding mode, and may use the default trapping mode or
|
|
// /SU. Both trapping modes are treated the same by M5; the
|
|
// only difference on the real hardware (as far a I can tell)
|
|
// is that without /SU you'd get an imprecise trap if you
|
|
// tried to compare a NaN with something else (instead of an
|
|
// "unordered" result).
|
|
1: decode FP_FULLFUNC {
|
|
format BasicOperateWithNopCheck {
|
|
0x0a5, 0x5a5: cmpteq({{ Fc = (Fa == Fb) ? 2.0 : 0.0; }},
|
|
FloatCmpOp);
|
|
0x0a7, 0x5a7: cmptle({{ Fc = (Fa <= Fb) ? 2.0 : 0.0; }},
|
|
FloatCmpOp);
|
|
0x0a6, 0x5a6: cmptlt({{ Fc = (Fa < Fb) ? 2.0 : 0.0; }},
|
|
FloatCmpOp);
|
|
0x0a4, 0x5a4: cmptun({{ // unordered
|
|
Fc = (!(Fa < Fb) && !(Fa == Fb) && !(Fa > Fb)) ? 2.0 : 0.0;
|
|
}}, FloatCmpOp);
|
|
}
|
|
}
|
|
|
|
// The FP-to-integer and integer-to-FP conversion insts
|
|
// require that FA be 31.
|
|
3: decode FA {
|
|
31: decode FP_TYPEFUNC {
|
|
format FloatingPointOperate {
|
|
0x2f: cvttq({{ Fc.sq = (int64_t)rint(Fb); }});
|
|
|
|
// The cvtts opcode is overloaded to be cvtst if the trap
|
|
// mode is 2 or 6 (which are not valid otherwise)
|
|
0x2c: decode FP_FULLFUNC {
|
|
format BasicOperateWithNopCheck {
|
|
// trap on denorm version "cvtst/s" is
|
|
// simulated same as cvtst
|
|
0x2ac, 0x6ac: cvtst({{ Fc = Fb.sf; }});
|
|
}
|
|
default: cvtts({{ Fc.sf = Fb; }});
|
|
}
|
|
|
|
// The trapping mode for integer-to-FP conversions
|
|
// must be /SUI or nothing; /U and /SU are not
|
|
// allowed. The full set of rounding modes are
|
|
// supported though.
|
|
0x3c: decode FP_TRAPMODE {
|
|
0,7: cvtqs({{ Fc.sf = Fb.sq; }});
|
|
}
|
|
0x3e: decode FP_TRAPMODE {
|
|
0,7: cvtqt({{ Fc = Fb.sq; }});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// misc FP operate
|
|
0x17: decode FP_FULLFUNC {
|
|
format BasicOperateWithNopCheck {
|
|
0x010: cvtlq({{
|
|
Fc.sl = (Fb.uq<63:62> << 30) | Fb.uq<58:29>;
|
|
}});
|
|
0x030: cvtql({{
|
|
Fc.uq = (Fb.uq<31:30> << 62) | (Fb.uq<29:0> << 29);
|
|
}});
|
|
|
|
// We treat the precise & imprecise trapping versions of
|
|
// cvtql identically.
|
|
0x130, 0x530: cvtqlv({{
|
|
// To avoid overflow, all the upper 32 bits must match
|
|
// the sign bit of the lower 32. We code this as
|
|
// checking the upper 33 bits for all 0s or all 1s.
|
|
uint64_t sign_bits = Fb.uq<63:31>;
|
|
if (sign_bits != 0 && sign_bits != mask(33))
|
|
fault = Integer_Overflow_Fault;
|
|
Fc.uq = (Fb.uq<31:30> << 62) | (Fb.uq<29:0> << 29);
|
|
}});
|
|
|
|
0x020: cpys({{ // copy sign
|
|
Fc.uq = (Fa.uq<63:> << 63) | Fb.uq<62:0>;
|
|
}});
|
|
0x021: cpysn({{ // copy sign negated
|
|
Fc.uq = (~Fa.uq<63:> << 63) | Fb.uq<62:0>;
|
|
}});
|
|
0x022: cpyse({{ // copy sign and exponent
|
|
Fc.uq = (Fa.uq<63:52> << 52) | Fb.uq<51:0>;
|
|
}});
|
|
|
|
0x02a: fcmoveq({{ Fc = (Fa == 0) ? Fb : Fc; }});
|
|
0x02b: fcmovne({{ Fc = (Fa != 0) ? Fb : Fc; }});
|
|
0x02c: fcmovlt({{ Fc = (Fa < 0) ? Fb : Fc; }});
|
|
0x02d: fcmovge({{ Fc = (Fa >= 0) ? Fb : Fc; }});
|
|
0x02e: fcmovle({{ Fc = (Fa <= 0) ? Fb : Fc; }});
|
|
0x02f: fcmovgt({{ Fc = (Fa > 0) ? Fb : Fc; }});
|
|
|
|
0x024: mt_fpcr({{ FPCR = Fa.uq; }});
|
|
0x025: mf_fpcr({{ Fa.uq = FPCR; }});
|
|
}
|
|
}
|
|
|
|
// miscellaneous mem-format ops
|
|
0x18: decode MEMFUNC {
|
|
format WarnUnimpl {
|
|
0x8000: fetch();
|
|
0xa000: fetch_m();
|
|
0xe800: ecb();
|
|
}
|
|
|
|
format MiscPrefetch {
|
|
0xf800: wh64({{ EA = Rb & ~ULL(63); }},
|
|
{{ xc->writeHint(EA, 64, memAccessFlags); }},
|
|
IsMemRef, IsDataPrefetch, IsStore, MemWriteOp,
|
|
NO_FAULT);
|
|
}
|
|
|
|
format BasicOperate {
|
|
0xc000: rpcc({{
|
|
#ifdef FULL_SYSTEM
|
|
/* Rb is a fake dependency so here is a fun way to get
|
|
* the parser to understand that.
|
|
*/
|
|
Ra = xc->readIpr(AlphaISA::IPR_CC, fault) + (Rb & 0);
|
|
|
|
#else
|
|
Ra = curTick;
|
|
#endif
|
|
}});
|
|
|
|
// All of the barrier instructions below do nothing in
|
|
// their execute() methods (hence the empty code blocks).
|
|
// All of their functionality is hard-coded in the
|
|
// pipeline based on the flags IsSerializing,
|
|
// IsMemBarrier, and IsWriteBarrier. In the current
|
|
// detailed CPU model, the execute() function only gets
|
|
// called at fetch, so there's no way to generate pipeline
|
|
// behavior at any other stage. Once we go to an
|
|
// exec-in-exec CPU model we should be able to get rid of
|
|
// these flags and implement this behavior via the
|
|
// execute() methods.
|
|
|
|
// trapb is just a barrier on integer traps, where excb is
|
|
// a barrier on integer and FP traps. "EXCB is thus a
|
|
// superset of TRAPB." (Alpha ARM, Sec 4.11.4) We treat
|
|
// them the same though.
|
|
0x0000: trapb({{ }}, IsSerializing, No_OpClass);
|
|
0x0400: excb({{ }}, IsSerializing, No_OpClass);
|
|
0x4000: mb({{ }}, IsMemBarrier, MemReadOp);
|
|
0x4400: wmb({{ }}, IsWriteBarrier, MemWriteOp);
|
|
}
|
|
|
|
#ifdef FULL_SYSTEM
|
|
format BasicOperate {
|
|
0xe000: rc({{
|
|
Ra = xc->readIntrFlag();
|
|
xc->setIntrFlag(0);
|
|
}}, IsNonSpeculative);
|
|
0xf000: rs({{
|
|
Ra = xc->readIntrFlag();
|
|
xc->setIntrFlag(1);
|
|
}}, IsNonSpeculative);
|
|
}
|
|
#else
|
|
format FailUnimpl {
|
|
0xe000: rc();
|
|
0xf000: rs();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef FULL_SYSTEM
|
|
0x00: CallPal::call_pal({{
|
|
if (!palValid ||
|
|
(palPriv
|
|
&& xc->readIpr(AlphaISA::IPR_ICM, fault) != AlphaISA::mode_kernel)) {
|
|
// invalid pal function code, or attempt to do privileged
|
|
// PAL call in non-kernel mode
|
|
fault = Unimplemented_Opcode_Fault;
|
|
}
|
|
else {
|
|
// check to see if simulator wants to do something special
|
|
// on this PAL call (including maybe suppress it)
|
|
bool dopal = xc->simPalCheck(palFunc);
|
|
|
|
if (dopal) {
|
|
AlphaISA::swap_palshadow(&xc->xcBase()->regs, true);
|
|
xc->setIpr(AlphaISA::IPR_EXC_ADDR, NPC);
|
|
NPC = xc->readIpr(AlphaISA::IPR_PAL_BASE, fault) + palOffset;
|
|
}
|
|
}
|
|
}}, IsNonSpeculative);
|
|
#else
|
|
0x00: decode PALFUNC {
|
|
format EmulatedCallPal {
|
|
0x00: halt ({{
|
|
SimExit(curTick, "halt instruction encountered");
|
|
}}, IsNonSpeculative);
|
|
0x83: callsys({{
|
|
xc->syscall();
|
|
}}, IsNonSpeculative);
|
|
// Read uniq reg into ABI return value register (r0)
|
|
0x9e: rduniq({{ R0 = Runiq; }});
|
|
// Write uniq reg with value from ABI arg register (r16)
|
|
0x9f: wruniq({{ Runiq = R16; }});
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef FULL_SYSTEM
|
|
format HwLoadStore {
|
|
0x1b: decode HW_LDST_QUAD {
|
|
0: hw_ld({{ EA = (Rb + disp) & ~3; }}, {{ Ra = Mem.ul; }}, L);
|
|
1: hw_ld({{ EA = (Rb + disp) & ~7; }}, {{ Ra = Mem.uq; }}, Q);
|
|
}
|
|
|
|
0x1f: decode HW_LDST_COND {
|
|
0: decode HW_LDST_QUAD {
|
|
0: hw_st({{ EA = (Rb + disp) & ~3; }},
|
|
{{ Mem.ul = Ra<31:0>; }}, L);
|
|
1: hw_st({{ EA = (Rb + disp) & ~7; }},
|
|
{{ Mem.uq = Ra.uq; }}, Q);
|
|
}
|
|
|
|
1: FailUnimpl::hw_st_cond();
|
|
}
|
|
}
|
|
|
|
format BasicOperate {
|
|
0x1e: hw_rei({{ xc->hwrei(); }});
|
|
|
|
// M5 special opcodes use the reserved 0x01 opcode space
|
|
0x01: decode M5FUNC {
|
|
0x00: arm({{
|
|
AlphaPseudo::arm(xc->xcBase());
|
|
}}, IsNonSpeculative);
|
|
0x01: quiesce({{
|
|
AlphaPseudo::quiesce(xc->xcBase());
|
|
}}, IsNonSpeculative);
|
|
0x10: ivlb({{
|
|
AlphaPseudo::ivlb(xc->xcBase());
|
|
}}, No_OpClass, IsNonSpeculative);
|
|
0x11: ivle({{
|
|
AlphaPseudo::ivle(xc->xcBase());
|
|
}}, No_OpClass, IsNonSpeculative);
|
|
0x20: m5exit_old({{
|
|
AlphaPseudo::m5exit_old(xc->xcBase());
|
|
}}, No_OpClass, IsNonSpeculative);
|
|
0x21: m5exit({{
|
|
AlphaPseudo::m5exit(xc->xcBase());
|
|
}}, No_OpClass, IsNonSpeculative);
|
|
0x30: initparam({{ Ra = xc->xcBase()->cpu->system->init_param; }});
|
|
0x40: resetstats({{
|
|
AlphaPseudo::resetstats(xc->xcBase());
|
|
}}, IsNonSpeculative);
|
|
0x41: dumpstats({{
|
|
AlphaPseudo::dumpstats(xc->xcBase());
|
|
}}, IsNonSpeculative);
|
|
0x42: dumpresetstats({{
|
|
AlphaPseudo::dumpresetstats(xc->xcBase());
|
|
}}, IsNonSpeculative);
|
|
0x43: m5checkpoint({{
|
|
AlphaPseudo::m5checkpoint(xc->xcBase());
|
|
}}, IsNonSpeculative);
|
|
0x50: m5readfile({{
|
|
AlphaPseudo::readfile(xc->xcBase());
|
|
}}, IsNonSpeculative);
|
|
0x51: m5break({{
|
|
AlphaPseudo::debugbreak(xc->xcBase());
|
|
}}, IsNonSpeculative);
|
|
0x52: m5switchcpu({{
|
|
AlphaPseudo::switchcpu(xc->xcBase());
|
|
}}, IsNonSpeculative);
|
|
|
|
}
|
|
}
|
|
|
|
format HwMoveIPR {
|
|
0x19: hw_mfpr({{
|
|
// this instruction is only valid in PAL mode
|
|
if (!xc->inPalMode()) {
|
|
fault = Unimplemented_Opcode_Fault;
|
|
}
|
|
else {
|
|
Ra = xc->readIpr(ipr_index, fault);
|
|
}
|
|
}});
|
|
0x1d: hw_mtpr({{
|
|
// this instruction is only valid in PAL mode
|
|
if (!xc->inPalMode()) {
|
|
fault = Unimplemented_Opcode_Fault;
|
|
}
|
|
else {
|
|
xc->setIpr(ipr_index, Ra);
|
|
if (traceData) { traceData->setData(Ra); }
|
|
}
|
|
}});
|
|
}
|
|
#endif
|
|
}
|