mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-02-05 12:53:16 +08:00
6df01ab8ab
The defs.h header will take care of including the various config.h headers. For now, it's just config.h, but we'll add more when we integrate gnulib in. This header should be used instead of config.h, and should be the first include in every .c file. We won't rely on the old behavior where we expected files to include the port's sim-main.h which then includes the common sim-basics.h which then includes config.h. We have a ton of code that includes things before sim-main.h, and it sometimes needs to be that way. Creating a dedicated header avoids the ordering mess and implicit inclusion that shows up otherwise.
1219 lines
38 KiB
C
1219 lines
38 KiB
C
/* RISC-V simulator.
|
||
|
||
Copyright (C) 2005-2021 Free Software Foundation, Inc.
|
||
Contributed by Mike Frysinger.
|
||
|
||
This file is part of simulators.
|
||
|
||
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 3 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/>. */
|
||
|
||
/* This file contains the main simulator decoding logic. i.e. everything that
|
||
is architecture specific. */
|
||
|
||
/* This must come before any other includes. */
|
||
#include "defs.h"
|
||
|
||
#include <inttypes.h>
|
||
#include <time.h>
|
||
|
||
#include "sim-main.h"
|
||
#include "sim-syscall.h"
|
||
|
||
#include "opcode/riscv.h"
|
||
|
||
#include "gdb/sim-riscv.h"
|
||
|
||
#include "targ-vals.h"
|
||
|
||
#define TRACE_REG(cpu, reg) \
|
||
TRACE_REGISTER (cpu, "wrote %s = %#" PRIxTW, riscv_gpr_names_abi[reg], \
|
||
cpu->regs[reg])
|
||
|
||
static const struct riscv_opcode *riscv_hash[OP_MASK_OP + 1];
|
||
#define OP_HASH_IDX(i) ((i) & (riscv_insn_length (i) == 2 ? 0x3 : 0x7f))
|
||
|
||
#define RISCV_ASSERT_RV32(cpu, fmt, args...) \
|
||
do { \
|
||
if (RISCV_XLEN (cpu) != 32) \
|
||
{ \
|
||
SIM_DESC sd = CPU_STATE (cpu); \
|
||
TRACE_INSN (cpu, "RV32I-only " fmt, ## args); \
|
||
sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL); \
|
||
} \
|
||
} while (0)
|
||
|
||
#define RISCV_ASSERT_RV64(cpu, fmt, args...) \
|
||
do { \
|
||
if (RISCV_XLEN (cpu) != 64) \
|
||
{ \
|
||
SIM_DESC sd = CPU_STATE (cpu); \
|
||
TRACE_INSN (cpu, "RV64I-only " fmt, ## args); \
|
||
sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL); \
|
||
} \
|
||
} while (0)
|
||
|
||
static INLINE void
|
||
store_rd (SIM_CPU *cpu, int rd, unsigned_word val)
|
||
{
|
||
if (rd)
|
||
{
|
||
cpu->regs[rd] = val;
|
||
TRACE_REG (cpu, rd);
|
||
}
|
||
}
|
||
|
||
static INLINE unsigned_word
|
||
fetch_csr (SIM_CPU *cpu, const char *name, int csr, unsigned_word *reg)
|
||
{
|
||
/* Handle pseudo registers. */
|
||
switch (csr)
|
||
{
|
||
/* Allow certain registers only in respective modes. */
|
||
case CSR_CYCLEH:
|
||
case CSR_INSTRETH:
|
||
case CSR_TIMEH:
|
||
RISCV_ASSERT_RV32 (cpu, "CSR: %s", name);
|
||
break;
|
||
}
|
||
|
||
return *reg;
|
||
}
|
||
|
||
static INLINE void
|
||
store_csr (SIM_CPU *cpu, const char *name, int csr, unsigned_word *reg,
|
||
unsigned_word val)
|
||
{
|
||
switch (csr)
|
||
{
|
||
/* These are pseudo registers that modify sub-fields of fcsr. */
|
||
case CSR_FRM:
|
||
val &= 0x7;
|
||
*reg = val;
|
||
cpu->csr.fcsr = (cpu->csr.fcsr & ~0xe0) | (val << 5);
|
||
break;
|
||
case CSR_FFLAGS:
|
||
val &= 0x1f;
|
||
*reg = val;
|
||
cpu->csr.fcsr = (cpu->csr.fcsr & ~0x1f) | val;
|
||
break;
|
||
/* Keep the sub-fields in sync. */
|
||
case CSR_FCSR:
|
||
*reg = val;
|
||
cpu->csr.frm = (val >> 5) & 0x7;
|
||
cpu->csr.fflags = val & 0x1f;
|
||
break;
|
||
|
||
/* Allow certain registers only in respective modes. */
|
||
case CSR_CYCLEH:
|
||
case CSR_INSTRETH:
|
||
case CSR_TIMEH:
|
||
RISCV_ASSERT_RV32 (cpu, "CSR: %s", name);
|
||
|
||
/* All the rest are immutable. */
|
||
default:
|
||
val = *reg;
|
||
break;
|
||
}
|
||
|
||
TRACE_REGISTER (cpu, "wrote CSR %s = %#" PRIxTW, name, val);
|
||
}
|
||
|
||
static inline unsigned_word
|
||
ashiftrt (unsigned_word val, unsigned_word shift)
|
||
{
|
||
unsigned32 sign = (val & 0x80000000) ? ~(0xfffffffful >> shift) : 0;
|
||
return (val >> shift) | sign;
|
||
}
|
||
|
||
static inline unsigned_word
|
||
ashiftrt64 (unsigned_word val, unsigned_word shift)
|
||
{
|
||
unsigned64 sign =
|
||
(val & 0x8000000000000000ull) ? ~(0xffffffffffffffffull >> shift) : 0;
|
||
return (val >> shift) | sign;
|
||
}
|
||
|
||
static sim_cia
|
||
execute_i (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
|
||
{
|
||
SIM_DESC sd = CPU_STATE (cpu);
|
||
int rd = (iw >> OP_SH_RD) & OP_MASK_RD;
|
||
int rs1 = (iw >> OP_SH_RS1) & OP_MASK_RS1;
|
||
int rs2 = (iw >> OP_SH_RS2) & OP_MASK_RS2;
|
||
const char *rd_name = riscv_gpr_names_abi[rd];
|
||
const char *rs1_name = riscv_gpr_names_abi[rs1];
|
||
const char *rs2_name = riscv_gpr_names_abi[rs2];
|
||
unsigned int csr = (iw >> OP_SH_CSR) & OP_MASK_CSR;
|
||
unsigned_word i_imm = EXTRACT_ITYPE_IMM (iw);
|
||
unsigned_word u_imm = EXTRACT_UTYPE_IMM ((unsigned64) iw);
|
||
unsigned_word s_imm = EXTRACT_STYPE_IMM (iw);
|
||
unsigned_word sb_imm = EXTRACT_BTYPE_IMM (iw);
|
||
unsigned_word shamt_imm = ((iw >> OP_SH_SHAMT) & OP_MASK_SHAMT);
|
||
unsigned_word tmp;
|
||
sim_cia pc = cpu->pc + 4;
|
||
|
||
TRACE_EXTRACT (cpu,
|
||
"rd:%-2i:%-4s "
|
||
"rs1:%-2i:%-4s %0*" PRIxTW " "
|
||
"rs2:%-2i:%-4s %0*" PRIxTW " "
|
||
"match:%#x mask:%#x",
|
||
rd, rd_name,
|
||
rs1, rs1_name, (int) sizeof (unsigned_word) * 2, cpu->regs[rs1],
|
||
rs2, rs2_name, (int) sizeof (unsigned_word) * 2, cpu->regs[rs2],
|
||
(unsigned) op->match, (unsigned) op->mask);
|
||
|
||
switch (op->match)
|
||
{
|
||
case MATCH_ADD:
|
||
TRACE_INSN (cpu, "add %s, %s, %s; // %s = %s + %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
store_rd (cpu, rd, cpu->regs[rs1] + cpu->regs[rs2]);
|
||
break;
|
||
case MATCH_ADDW:
|
||
TRACE_INSN (cpu, "addw %s, %s, %s; // %s = %s + %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
|
||
store_rd (cpu, rd, EXTEND32 (cpu->regs[rs1] + cpu->regs[rs2]));
|
||
break;
|
||
case MATCH_ADDI:
|
||
TRACE_INSN (cpu, "addi %s, %s, %#" PRIxTW "; // %s = %s + %#" PRIxTW,
|
||
rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm);
|
||
store_rd (cpu, rd, cpu->regs[rs1] + i_imm);
|
||
break;
|
||
case MATCH_ADDIW:
|
||
TRACE_INSN (cpu, "addiw %s, %s, %#" PRIxTW "; // %s = %s + %#" PRIxTW,
|
||
rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm);
|
||
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
|
||
store_rd (cpu, rd, EXTEND32 (cpu->regs[rs1] + i_imm));
|
||
break;
|
||
case MATCH_AND:
|
||
TRACE_INSN (cpu, "and %s, %s, %s; // %s = %s & %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
store_rd (cpu, rd, cpu->regs[rs1] & cpu->regs[rs2]);
|
||
break;
|
||
case MATCH_ANDI:
|
||
TRACE_INSN (cpu, "andi %s, %s, %" PRIiTW "; // %s = %s & %#" PRIxTW,
|
||
rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm);
|
||
store_rd (cpu, rd, cpu->regs[rs1] & i_imm);
|
||
break;
|
||
case MATCH_OR:
|
||
TRACE_INSN (cpu, "or %s, %s, %s; // %s = %s | %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
store_rd (cpu, rd, cpu->regs[rs1] | cpu->regs[rs2]);
|
||
break;
|
||
case MATCH_ORI:
|
||
TRACE_INSN (cpu, "ori %s, %s, %" PRIiTW "; // %s = %s | %#" PRIxTW,
|
||
rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm);
|
||
store_rd (cpu, rd, cpu->regs[rs1] | i_imm);
|
||
break;
|
||
case MATCH_XOR:
|
||
TRACE_INSN (cpu, "xor %s, %s, %s; // %s = %s ^ %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
store_rd (cpu, rd, cpu->regs[rs1] ^ cpu->regs[rs2]);
|
||
break;
|
||
case MATCH_XORI:
|
||
TRACE_INSN (cpu, "xori %s, %s, %" PRIiTW "; // %s = %s ^ %#" PRIxTW,
|
||
rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm);
|
||
store_rd (cpu, rd, cpu->regs[rs1] ^ i_imm);
|
||
break;
|
||
case MATCH_SUB:
|
||
TRACE_INSN (cpu, "sub %s, %s, %s; // %s = %s - %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
store_rd (cpu, rd, cpu->regs[rs1] - cpu->regs[rs2]);
|
||
break;
|
||
case MATCH_SUBW:
|
||
TRACE_INSN (cpu, "subw %s, %s, %s; // %s = %s - %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
|
||
store_rd (cpu, rd, EXTEND32 (cpu->regs[rs1] - cpu->regs[rs2]));
|
||
break;
|
||
case MATCH_LUI:
|
||
TRACE_INSN (cpu, "lui %s, %#" PRIxTW ";", rd_name, u_imm);
|
||
store_rd (cpu, rd, u_imm);
|
||
break;
|
||
case MATCH_SLL:
|
||
TRACE_INSN (cpu, "sll %s, %s, %s; // %s = %s << %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
u_imm = RISCV_XLEN (cpu) == 32 ? 0x1f : 0x3f;
|
||
store_rd (cpu, rd, cpu->regs[rs1] << (cpu->regs[rs2] & u_imm));
|
||
break;
|
||
case MATCH_SLLW:
|
||
TRACE_INSN (cpu, "sllw %s, %s, %s; // %s = %s << %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
|
||
store_rd (cpu, rd, EXTEND32 (
|
||
(unsigned32) cpu->regs[rs1] << (cpu->regs[rs2] & 0x1f)));
|
||
break;
|
||
case MATCH_SLLI:
|
||
TRACE_INSN (cpu, "slli %s, %s, %" PRIiTW "; // %s = %s << %#" PRIxTW,
|
||
rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm);
|
||
if (RISCV_XLEN (cpu) == 32 && shamt_imm > 0x1f)
|
||
sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
|
||
store_rd (cpu, rd, cpu->regs[rs1] << shamt_imm);
|
||
break;
|
||
case MATCH_SLLIW:
|
||
TRACE_INSN (cpu, "slliw %s, %s, %" PRIiTW "; // %s = %s << %#" PRIxTW,
|
||
rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm);
|
||
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
|
||
store_rd (cpu, rd, EXTEND32 ((unsigned32) cpu->regs[rs1] << shamt_imm));
|
||
break;
|
||
case MATCH_SRL:
|
||
TRACE_INSN (cpu, "srl %s, %s, %s; // %s = %s >> %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
u_imm = RISCV_XLEN (cpu) == 32 ? 0x1f : 0x3f;
|
||
store_rd (cpu, rd, cpu->regs[rs1] >> (cpu->regs[rs2] & u_imm));
|
||
break;
|
||
case MATCH_SRLW:
|
||
TRACE_INSN (cpu, "srlw %s, %s, %s; // %s = %s >> %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
|
||
store_rd (cpu, rd, EXTEND32 (
|
||
(unsigned32) cpu->regs[rs1] >> (cpu->regs[rs2] & 0x1f)));
|
||
break;
|
||
case MATCH_SRLI:
|
||
TRACE_INSN (cpu, "srli %s, %s, %" PRIiTW "; // %s = %s >> %#" PRIxTW,
|
||
rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm);
|
||
if (RISCV_XLEN (cpu) == 32 && shamt_imm > 0x1f)
|
||
sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
|
||
store_rd (cpu, rd, cpu->regs[rs1] >> shamt_imm);
|
||
break;
|
||
case MATCH_SRLIW:
|
||
TRACE_INSN (cpu, "srliw %s, %s, %" PRIiTW "; // %s = %s >> %#" PRIxTW,
|
||
rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm);
|
||
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
|
||
store_rd (cpu, rd, EXTEND32 ((unsigned32) cpu->regs[rs1] >> shamt_imm));
|
||
break;
|
||
case MATCH_SRA:
|
||
TRACE_INSN (cpu, "sra %s, %s, %s; // %s = %s >>> %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
if (RISCV_XLEN (cpu) == 32)
|
||
tmp = ashiftrt (cpu->regs[rs1], cpu->regs[rs2] & 0x1f);
|
||
else
|
||
tmp = ashiftrt64 (cpu->regs[rs1], cpu->regs[rs2] & 0x3f);
|
||
store_rd (cpu, rd, tmp);
|
||
break;
|
||
case MATCH_SRAW:
|
||
TRACE_INSN (cpu, "sraw %s, %s, %s; // %s = %s >>> %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
|
||
store_rd (cpu, rd, EXTEND32 (
|
||
ashiftrt ((signed32) cpu->regs[rs1], cpu->regs[rs2] & 0x1f)));
|
||
break;
|
||
case MATCH_SRAI:
|
||
TRACE_INSN (cpu, "srai %s, %s, %" PRIiTW "; // %s = %s >>> %#" PRIxTW,
|
||
rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm);
|
||
if (RISCV_XLEN (cpu) == 32)
|
||
{
|
||
if (shamt_imm > 0x1f)
|
||
sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
|
||
tmp = ashiftrt (cpu->regs[rs1], shamt_imm);
|
||
}
|
||
else
|
||
tmp = ashiftrt64 (cpu->regs[rs1], shamt_imm);
|
||
store_rd (cpu, rd, tmp);
|
||
break;
|
||
case MATCH_SRAIW:
|
||
TRACE_INSN (cpu, "sraiw %s, %s, %" PRIiTW "; // %s = %s >>> %#" PRIxTW,
|
||
rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm);
|
||
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
|
||
store_rd (cpu, rd, EXTEND32 (
|
||
ashiftrt ((signed32) cpu->regs[rs1], shamt_imm)));
|
||
break;
|
||
case MATCH_SLT:
|
||
TRACE_INSN (cpu, "slt");
|
||
store_rd (cpu, rd,
|
||
!!((signed_word) cpu->regs[rs1] < (signed_word) cpu->regs[rs2]));
|
||
break;
|
||
case MATCH_SLTU:
|
||
TRACE_INSN (cpu, "sltu");
|
||
store_rd (cpu, rd, !!((unsigned_word) cpu->regs[rs1] <
|
||
(unsigned_word) cpu->regs[rs2]));
|
||
break;
|
||
case MATCH_SLTI:
|
||
TRACE_INSN (cpu, "slti");
|
||
store_rd (cpu, rd, !!((signed_word) cpu->regs[rs1] <
|
||
(signed_word) i_imm));
|
||
break;
|
||
case MATCH_SLTIU:
|
||
TRACE_INSN (cpu, "sltiu");
|
||
store_rd (cpu, rd, !!((unsigned_word) cpu->regs[rs1] <
|
||
(unsigned_word) i_imm));
|
||
break;
|
||
case MATCH_AUIPC:
|
||
TRACE_INSN (cpu, "auipc %s, %" PRIiTW "; // %s = pc + %" PRIiTW,
|
||
rd_name, u_imm, rd_name, u_imm);
|
||
store_rd (cpu, rd, cpu->pc + u_imm);
|
||
break;
|
||
case MATCH_BEQ:
|
||
TRACE_INSN (cpu, "beq %s, %s, %#" PRIxTW "; "
|
||
"// if (%s == %s) goto %#" PRIxTW,
|
||
rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm);
|
||
if (cpu->regs[rs1] == cpu->regs[rs2])
|
||
{
|
||
pc = cpu->pc + sb_imm;
|
||
TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
|
||
}
|
||
break;
|
||
case MATCH_BLT:
|
||
TRACE_INSN (cpu, "blt %s, %s, %#" PRIxTW "; "
|
||
"// if (%s < %s) goto %#" PRIxTW,
|
||
rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm);
|
||
if ((signed_word) cpu->regs[rs1] < (signed_word) cpu->regs[rs2])
|
||
{
|
||
pc = cpu->pc + sb_imm;
|
||
TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
|
||
}
|
||
break;
|
||
case MATCH_BLTU:
|
||
TRACE_INSN (cpu, "bltu %s, %s, %#" PRIxTW "; "
|
||
"// if (%s < %s) goto %#" PRIxTW,
|
||
rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm);
|
||
if ((unsigned_word) cpu->regs[rs1] < (unsigned_word) cpu->regs[rs2])
|
||
{
|
||
pc = cpu->pc + sb_imm;
|
||
TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
|
||
}
|
||
break;
|
||
case MATCH_BGE:
|
||
TRACE_INSN (cpu, "bge %s, %s, %#" PRIxTW "; "
|
||
"// if (%s >= %s) goto %#" PRIxTW,
|
||
rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm);
|
||
if ((signed_word) cpu->regs[rs1] >= (signed_word) cpu->regs[rs2])
|
||
{
|
||
pc = cpu->pc + sb_imm;
|
||
TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
|
||
}
|
||
break;
|
||
case MATCH_BGEU:
|
||
TRACE_INSN (cpu, "bgeu %s, %s, %#" PRIxTW "; "
|
||
"// if (%s >= %s) goto %#" PRIxTW,
|
||
rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm);
|
||
if ((unsigned_word) cpu->regs[rs1] >= (unsigned_word) cpu->regs[rs2])
|
||
{
|
||
pc = cpu->pc + sb_imm;
|
||
TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
|
||
}
|
||
break;
|
||
case MATCH_BNE:
|
||
TRACE_INSN (cpu, "bne %s, %s, %#" PRIxTW "; "
|
||
"// if (%s != %s) goto %#" PRIxTW,
|
||
rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm);
|
||
if (cpu->regs[rs1] != cpu->regs[rs2])
|
||
{
|
||
pc = cpu->pc + sb_imm;
|
||
TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
|
||
}
|
||
break;
|
||
case MATCH_JAL:
|
||
TRACE_INSN (cpu, "jal %s, %" PRIiTW ";", rd_name,
|
||
EXTRACT_JTYPE_IMM (iw));
|
||
store_rd (cpu, rd, cpu->pc + 4);
|
||
pc = cpu->pc + EXTRACT_JTYPE_IMM (iw);
|
||
TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
|
||
break;
|
||
case MATCH_JALR:
|
||
TRACE_INSN (cpu, "jalr %s, %s, %" PRIiTW ";", rd_name, rs1_name, i_imm);
|
||
store_rd (cpu, rd, cpu->pc + 4);
|
||
pc = cpu->regs[rs1] + i_imm;
|
||
TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
|
||
break;
|
||
|
||
case MATCH_LD:
|
||
TRACE_INSN (cpu, "ld %s, %" PRIiTW "(%s);",
|
||
rd_name, i_imm, rs1_name);
|
||
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
|
||
store_rd (cpu, rd,
|
||
sim_core_read_unaligned_8 (cpu, cpu->pc, read_map,
|
||
cpu->regs[rs1] + i_imm));
|
||
break;
|
||
case MATCH_LW:
|
||
TRACE_INSN (cpu, "lw %s, %" PRIiTW "(%s);",
|
||
rd_name, i_imm, rs1_name);
|
||
store_rd (cpu, rd, EXTEND32 (
|
||
sim_core_read_unaligned_4 (cpu, cpu->pc, read_map,
|
||
cpu->regs[rs1] + i_imm)));
|
||
break;
|
||
case MATCH_LWU:
|
||
TRACE_INSN (cpu, "lwu %s, %" PRIiTW "(%s);",
|
||
rd_name, i_imm, rs1_name);
|
||
store_rd (cpu, rd,
|
||
sim_core_read_unaligned_4 (cpu, cpu->pc, read_map,
|
||
cpu->regs[rs1] + i_imm));
|
||
break;
|
||
case MATCH_LH:
|
||
TRACE_INSN (cpu, "lh %s, %" PRIiTW "(%s);",
|
||
rd_name, i_imm, rs1_name);
|
||
store_rd (cpu, rd, EXTEND16 (
|
||
sim_core_read_unaligned_2 (cpu, cpu->pc, read_map,
|
||
cpu->regs[rs1] + i_imm)));
|
||
break;
|
||
case MATCH_LHU:
|
||
TRACE_INSN (cpu, "lbu %s, %" PRIiTW "(%s);",
|
||
rd_name, i_imm, rs1_name);
|
||
store_rd (cpu, rd,
|
||
sim_core_read_unaligned_2 (cpu, cpu->pc, read_map,
|
||
cpu->regs[rs1] + i_imm));
|
||
break;
|
||
case MATCH_LB:
|
||
TRACE_INSN (cpu, "lb %s, %" PRIiTW "(%s);",
|
||
rd_name, i_imm, rs1_name);
|
||
store_rd (cpu, rd, EXTEND8 (
|
||
sim_core_read_unaligned_1 (cpu, cpu->pc, read_map,
|
||
cpu->regs[rs1] + i_imm)));
|
||
break;
|
||
case MATCH_LBU:
|
||
TRACE_INSN (cpu, "lbu %s, %" PRIiTW "(%s);",
|
||
rd_name, i_imm, rs1_name);
|
||
store_rd (cpu, rd,
|
||
sim_core_read_unaligned_1 (cpu, cpu->pc, read_map,
|
||
cpu->regs[rs1] + i_imm));
|
||
break;
|
||
case MATCH_SD:
|
||
TRACE_INSN (cpu, "sd %s, %" PRIiTW "(%s);",
|
||
rs2_name, s_imm, rs1_name);
|
||
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
|
||
sim_core_write_unaligned_8 (cpu, cpu->pc, write_map,
|
||
cpu->regs[rs1] + s_imm, cpu->regs[rs2]);
|
||
break;
|
||
case MATCH_SW:
|
||
TRACE_INSN (cpu, "sw %s, %" PRIiTW "(%s);",
|
||
rs2_name, s_imm, rs1_name);
|
||
sim_core_write_unaligned_4 (cpu, cpu->pc, write_map,
|
||
cpu->regs[rs1] + s_imm, cpu->regs[rs2]);
|
||
break;
|
||
case MATCH_SH:
|
||
TRACE_INSN (cpu, "sh %s, %" PRIiTW "(%s);",
|
||
rs2_name, s_imm, rs1_name);
|
||
sim_core_write_unaligned_2 (cpu, cpu->pc, write_map,
|
||
cpu->regs[rs1] + s_imm, cpu->regs[rs2]);
|
||
break;
|
||
case MATCH_SB:
|
||
TRACE_INSN (cpu, "sb %s, %" PRIiTW "(%s);",
|
||
rs2_name, s_imm, rs1_name);
|
||
sim_core_write_unaligned_1 (cpu, cpu->pc, write_map,
|
||
cpu->regs[rs1] + s_imm, cpu->regs[rs2]);
|
||
break;
|
||
|
||
case MATCH_CSRRC:
|
||
TRACE_INSN (cpu, "csrrc");
|
||
switch (csr)
|
||
{
|
||
#define DECLARE_CSR(name, num, ...) \
|
||
case num: \
|
||
store_rd (cpu, rd, fetch_csr (cpu, #name, num, &cpu->csr.name)); \
|
||
store_csr (cpu, #name, num, &cpu->csr.name, \
|
||
cpu->csr.name & !cpu->regs[rs1]); \
|
||
break;
|
||
#include "opcode/riscv-opc.h"
|
||
#undef DECLARE_CSR
|
||
}
|
||
break;
|
||
case MATCH_CSRRS:
|
||
TRACE_INSN (cpu, "csrrs");
|
||
switch (csr)
|
||
{
|
||
#define DECLARE_CSR(name, num, ...) \
|
||
case num: \
|
||
store_rd (cpu, rd, fetch_csr (cpu, #name, num, &cpu->csr.name)); \
|
||
store_csr (cpu, #name, num, &cpu->csr.name, \
|
||
cpu->csr.name | cpu->regs[rs1]); \
|
||
break;
|
||
#include "opcode/riscv-opc.h"
|
||
#undef DECLARE_CSR
|
||
}
|
||
break;
|
||
case MATCH_CSRRW:
|
||
TRACE_INSN (cpu, "csrrw");
|
||
switch (csr)
|
||
{
|
||
#define DECLARE_CSR(name, num, ...) \
|
||
case num: \
|
||
store_rd (cpu, rd, fetch_csr (cpu, #name, num, &cpu->csr.name)); \
|
||
store_csr (cpu, #name, num, &cpu->csr.name, cpu->regs[rs1]); \
|
||
break;
|
||
#include "opcode/riscv-opc.h"
|
||
#undef DECLARE_CSR
|
||
}
|
||
break;
|
||
|
||
case MATCH_RDCYCLE:
|
||
TRACE_INSN (cpu, "rdcycle %s;", rd_name);
|
||
store_rd (cpu, rd, fetch_csr (cpu, "cycle", CSR_CYCLE, &cpu->csr.cycle));
|
||
break;
|
||
case MATCH_RDCYCLEH:
|
||
TRACE_INSN (cpu, "rdcycleh %s;", rd_name);
|
||
RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name);
|
||
store_rd (cpu, rd,
|
||
fetch_csr (cpu, "cycleh", CSR_CYCLEH, &cpu->csr.cycleh));
|
||
break;
|
||
case MATCH_RDINSTRET:
|
||
TRACE_INSN (cpu, "rdinstret %s;", rd_name);
|
||
store_rd (cpu, rd,
|
||
fetch_csr (cpu, "instret", CSR_INSTRET, &cpu->csr.instret));
|
||
break;
|
||
case MATCH_RDINSTRETH:
|
||
TRACE_INSN (cpu, "rdinstreth %s;", rd_name);
|
||
RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name);
|
||
store_rd (cpu, rd,
|
||
fetch_csr (cpu, "instreth", CSR_INSTRETH, &cpu->csr.instreth));
|
||
break;
|
||
case MATCH_RDTIME:
|
||
TRACE_INSN (cpu, "rdtime %s;", rd_name);
|
||
store_rd (cpu, rd, fetch_csr (cpu, "time", CSR_TIME, &cpu->csr.time));
|
||
break;
|
||
case MATCH_RDTIMEH:
|
||
TRACE_INSN (cpu, "rdtimeh %s;", rd_name);
|
||
RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name);
|
||
store_rd (cpu, rd, fetch_csr (cpu, "timeh", CSR_TIMEH, &cpu->csr.timeh));
|
||
break;
|
||
|
||
case MATCH_FENCE:
|
||
TRACE_INSN (cpu, "fence;");
|
||
break;
|
||
case MATCH_FENCE_I:
|
||
TRACE_INSN (cpu, "fence.i;");
|
||
break;
|
||
case MATCH_SBREAK:
|
||
TRACE_INSN (cpu, "sbreak;");
|
||
/* GDB expects us to step over SBREAK. */
|
||
sim_engine_halt (sd, cpu, NULL, cpu->pc + 4, sim_stopped, SIM_SIGTRAP);
|
||
break;
|
||
case MATCH_ECALL:
|
||
TRACE_INSN (cpu, "ecall;");
|
||
cpu->a0 = sim_syscall (cpu, cpu->a7, cpu->a0, cpu->a1, cpu->a2, cpu->a3);
|
||
break;
|
||
default:
|
||
TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
|
||
sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
|
||
}
|
||
|
||
return pc;
|
||
}
|
||
|
||
static unsigned64
|
||
mulhu (unsigned64 a, unsigned64 b)
|
||
{
|
||
#ifdef HAVE___INT128
|
||
return ((__int128)a * b) >> 64;
|
||
#else
|
||
uint64_t t;
|
||
uint32_t y1, y2, y3;
|
||
uint64_t a0 = (uint32_t)a, a1 = a >> 32;
|
||
uint64_t b0 = (uint32_t)b, b1 = b >> 32;
|
||
|
||
t = a1*b0 + ((a0*b0) >> 32);
|
||
y1 = t;
|
||
y2 = t >> 32;
|
||
|
||
t = a0*b1 + y1;
|
||
y1 = t;
|
||
|
||
t = a1*b1 + y2 + (t >> 32);
|
||
y2 = t;
|
||
y3 = t >> 32;
|
||
|
||
return ((uint64_t)y3 << 32) | y2;
|
||
#endif
|
||
}
|
||
|
||
static unsigned64
|
||
mulh (signed64 a, signed64 b)
|
||
{
|
||
int negate = (a < 0) != (b < 0);
|
||
uint64_t res = mulhu (a < 0 ? -a : a, b < 0 ? -b : b);
|
||
return negate ? ~res + (a * b == 0) : res;
|
||
}
|
||
|
||
static unsigned64
|
||
mulhsu (signed64 a, unsigned64 b)
|
||
{
|
||
int negate = a < 0;
|
||
uint64_t res = mulhu (a < 0 ? -a : a, b);
|
||
return negate ? ~res + (a * b == 0) : res;
|
||
}
|
||
|
||
static sim_cia
|
||
execute_m (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
|
||
{
|
||
SIM_DESC sd = CPU_STATE (cpu);
|
||
int rd = (iw >> OP_SH_RD) & OP_MASK_RD;
|
||
int rs1 = (iw >> OP_SH_RS1) & OP_MASK_RS1;
|
||
int rs2 = (iw >> OP_SH_RS2) & OP_MASK_RS2;
|
||
const char *rd_name = riscv_gpr_names_abi[rd];
|
||
const char *rs1_name = riscv_gpr_names_abi[rs1];
|
||
const char *rs2_name = riscv_gpr_names_abi[rs2];
|
||
unsigned_word tmp, dividend_max;
|
||
sim_cia pc = cpu->pc + 4;
|
||
|
||
dividend_max = -((unsigned_word) 1 << (WITH_TARGET_WORD_BITSIZE - 1));
|
||
|
||
switch (op->match)
|
||
{
|
||
case MATCH_DIV:
|
||
TRACE_INSN (cpu, "div %s, %s, %s; // %s = %s / %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
if (cpu->regs[rs1] == dividend_max && cpu->regs[rs2] == -1)
|
||
tmp = dividend_max;
|
||
else if (cpu->regs[rs2])
|
||
tmp = (signed_word) cpu->regs[rs1] / (signed_word) cpu->regs[rs2];
|
||
else
|
||
tmp = -1;
|
||
store_rd (cpu, rd, tmp);
|
||
break;
|
||
case MATCH_DIVW:
|
||
TRACE_INSN (cpu, "divw %s, %s, %s; // %s = %s / %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
|
||
if (EXTEND32 (cpu->regs[rs2]) == -1)
|
||
tmp = 1 << 31;
|
||
else if (EXTEND32 (cpu->regs[rs2]))
|
||
tmp = EXTEND32 (cpu->regs[rs1]) / EXTEND32 (cpu->regs[rs2]);
|
||
else
|
||
tmp = -1;
|
||
store_rd (cpu, rd, EXTEND32 (tmp));
|
||
break;
|
||
case MATCH_DIVU:
|
||
TRACE_INSN (cpu, "divu %s, %s, %s; // %s = %s / %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
if (cpu->regs[rs2])
|
||
store_rd (cpu, rd, (unsigned_word) cpu->regs[rs1]
|
||
/ (unsigned_word) cpu->regs[rs2]);
|
||
else
|
||
store_rd (cpu, rd, -1);
|
||
break;
|
||
case MATCH_DIVUW:
|
||
TRACE_INSN (cpu, "divuw %s, %s, %s; // %s = %s / %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
|
||
if ((unsigned32) cpu->regs[rs2])
|
||
tmp = (unsigned32) cpu->regs[rs1] / (unsigned32) cpu->regs[rs2];
|
||
else
|
||
tmp = -1;
|
||
store_rd (cpu, rd, EXTEND32 (tmp));
|
||
break;
|
||
case MATCH_MUL:
|
||
TRACE_INSN (cpu, "mul %s, %s, %s; // %s = %s * %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
store_rd (cpu, rd, cpu->regs[rs1] * cpu->regs[rs2]);
|
||
break;
|
||
case MATCH_MULW:
|
||
TRACE_INSN (cpu, "mulw %s, %s, %s; // %s = %s * %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
|
||
store_rd (cpu, rd, EXTEND32 ((signed32) cpu->regs[rs1]
|
||
* (signed32) cpu->regs[rs2]));
|
||
break;
|
||
case MATCH_MULH:
|
||
TRACE_INSN (cpu, "mulh %s, %s, %s; // %s = %s * %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
if (RISCV_XLEN (cpu) == 32)
|
||
store_rd (cpu, rd, ((signed64)(signed_word) cpu->regs[rs1]
|
||
* (signed64)(signed_word) cpu->regs[rs2]) >> 32);
|
||
else
|
||
store_rd (cpu, rd, mulh (cpu->regs[rs1], cpu->regs[rs2]));
|
||
break;
|
||
case MATCH_MULHU:
|
||
TRACE_INSN (cpu, "mulhu %s, %s, %s; // %s = %s * %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
if (RISCV_XLEN (cpu) == 32)
|
||
store_rd (cpu, rd, ((unsigned64)cpu->regs[rs1]
|
||
* (unsigned64)cpu->regs[rs2]) >> 32);
|
||
else
|
||
store_rd (cpu, rd, mulhu (cpu->regs[rs1], cpu->regs[rs2]));
|
||
break;
|
||
case MATCH_MULHSU:
|
||
TRACE_INSN (cpu, "mulhsu %s, %s, %s; // %s = %s * %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
if (RISCV_XLEN (cpu) == 32)
|
||
store_rd (cpu, rd, ((signed64)(signed_word) cpu->regs[rs1]
|
||
* (unsigned64)cpu->regs[rs2]) >> 32);
|
||
else
|
||
store_rd (cpu, rd, mulhsu (cpu->regs[rs1], cpu->regs[rs2]));
|
||
break;
|
||
case MATCH_REM:
|
||
TRACE_INSN (cpu, "rem %s, %s, %s; // %s = %s %% %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
if (cpu->regs[rs1] == dividend_max && cpu->regs[rs2] == -1)
|
||
tmp = 0;
|
||
else if (cpu->regs[rs2])
|
||
tmp = (signed_word) cpu->regs[rs1] % (signed_word) cpu->regs[rs2];
|
||
else
|
||
tmp = cpu->regs[rs1];
|
||
store_rd (cpu, rd, tmp);
|
||
break;
|
||
case MATCH_REMW:
|
||
TRACE_INSN (cpu, "remw %s, %s, %s; // %s = %s %% %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
|
||
if (EXTEND32 (cpu->regs[rs2]) == -1)
|
||
tmp = 0;
|
||
else if (EXTEND32 (cpu->regs[rs2]))
|
||
tmp = EXTEND32 (cpu->regs[rs1]) % EXTEND32 (cpu->regs[rs2]);
|
||
else
|
||
tmp = cpu->regs[rs1];
|
||
store_rd (cpu, rd, EXTEND32 (tmp));
|
||
break;
|
||
case MATCH_REMU:
|
||
TRACE_INSN (cpu, "remu %s, %s, %s; // %s = %s %% %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
if (cpu->regs[rs2])
|
||
store_rd (cpu, rd, cpu->regs[rs1] % cpu->regs[rs2]);
|
||
else
|
||
store_rd (cpu, rd, cpu->regs[rs1]);
|
||
break;
|
||
case MATCH_REMUW:
|
||
TRACE_INSN (cpu, "remuw %s, %s, %s; // %s = %s %% %s",
|
||
rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name);
|
||
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
|
||
if ((unsigned32) cpu->regs[rs2])
|
||
tmp = (unsigned32) cpu->regs[rs1] % (unsigned32) cpu->regs[rs2];
|
||
else
|
||
tmp = cpu->regs[rs1];
|
||
store_rd (cpu, rd, EXTEND32 (tmp));
|
||
break;
|
||
default:
|
||
TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
|
||
sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
|
||
}
|
||
|
||
return pc;
|
||
}
|
||
|
||
static sim_cia
|
||
execute_a (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
|
||
{
|
||
SIM_DESC sd = CPU_STATE (cpu);
|
||
int rd = (iw >> OP_SH_RD) & OP_MASK_RD;
|
||
int rs1 = (iw >> OP_SH_RS1) & OP_MASK_RS1;
|
||
int rs2 = (iw >> OP_SH_RS2) & OP_MASK_RS2;
|
||
const char *rd_name = riscv_gpr_names_abi[rd];
|
||
const char *rs1_name = riscv_gpr_names_abi[rs1];
|
||
const char *rs2_name = riscv_gpr_names_abi[rs2];
|
||
struct atomic_mem_reserved_list *amo_prev, *amo_curr;
|
||
unsigned_word tmp;
|
||
sim_cia pc = cpu->pc + 4;
|
||
|
||
/* Handle these two load/store operations specifically. */
|
||
switch (op->match)
|
||
{
|
||
case MATCH_LR_W:
|
||
TRACE_INSN (cpu, "%s %s, (%s);", op->name, rd_name, rs1_name);
|
||
store_rd (cpu, rd,
|
||
sim_core_read_unaligned_4 (cpu, cpu->pc, read_map, cpu->regs[rs1]));
|
||
|
||
/* Walk the reservation list to find an existing match. */
|
||
amo_curr = sd->amo_reserved_list;
|
||
while (amo_curr)
|
||
{
|
||
if (amo_curr->addr == cpu->regs[rs1])
|
||
goto done;
|
||
amo_curr = amo_curr->next;
|
||
}
|
||
|
||
/* No reservation exists, so add one. */
|
||
amo_curr = xmalloc (sizeof (*amo_curr));
|
||
amo_curr->addr = cpu->regs[rs1];
|
||
amo_curr->next = sd->amo_reserved_list;
|
||
sd->amo_reserved_list = amo_curr;
|
||
goto done;
|
||
case MATCH_SC_W:
|
||
TRACE_INSN (cpu, "%s %s, %s, (%s);", op->name, rd_name, rs2_name,
|
||
rs1_name);
|
||
|
||
/* Walk the reservation list to find a match. */
|
||
amo_curr = amo_prev = sd->amo_reserved_list;
|
||
while (amo_curr)
|
||
{
|
||
if (amo_curr->addr == cpu->regs[rs1])
|
||
{
|
||
/* We found a reservation, so operate it. */
|
||
sim_core_write_unaligned_4 (cpu, cpu->pc, write_map,
|
||
cpu->regs[rs1], cpu->regs[rs2]);
|
||
store_rd (cpu, rd, 0);
|
||
if (amo_curr == sd->amo_reserved_list)
|
||
sd->amo_reserved_list = amo_curr->next;
|
||
else
|
||
amo_prev->next = amo_curr->next;
|
||
free (amo_curr);
|
||
goto done;
|
||
}
|
||
amo_prev = amo_curr;
|
||
amo_curr = amo_curr->next;
|
||
}
|
||
|
||
/* If we're still here, then no reservation exists, so mark as failed. */
|
||
store_rd (cpu, rd, 1);
|
||
goto done;
|
||
}
|
||
|
||
/* Handle the rest of the atomic insns with common code paths. */
|
||
TRACE_INSN (cpu, "%s %s, %s, (%s);",
|
||
op->name, rd_name, rs2_name, rs1_name);
|
||
if (op->xlen_requirement == 64)
|
||
tmp = sim_core_read_unaligned_8 (cpu, cpu->pc, read_map, cpu->regs[rs1]);
|
||
else
|
||
tmp = EXTEND32 (sim_core_read_unaligned_4 (cpu, cpu->pc, read_map,
|
||
cpu->regs[rs1]));
|
||
store_rd (cpu, rd, tmp);
|
||
|
||
switch (op->match)
|
||
{
|
||
case MATCH_AMOADD_D:
|
||
case MATCH_AMOADD_W:
|
||
tmp = cpu->regs[rd] + cpu->regs[rs2];
|
||
break;
|
||
case MATCH_AMOAND_D:
|
||
case MATCH_AMOAND_W:
|
||
tmp = cpu->regs[rd] & cpu->regs[rs2];
|
||
break;
|
||
case MATCH_AMOMAX_D:
|
||
case MATCH_AMOMAX_W:
|
||
tmp = max ((signed_word) cpu->regs[rd], (signed_word) cpu->regs[rs2]);
|
||
break;
|
||
case MATCH_AMOMAXU_D:
|
||
case MATCH_AMOMAXU_W:
|
||
tmp = max ((unsigned_word) cpu->regs[rd], (unsigned_word) cpu->regs[rs2]);
|
||
break;
|
||
case MATCH_AMOMIN_D:
|
||
case MATCH_AMOMIN_W:
|
||
tmp = min ((signed_word) cpu->regs[rd], (signed_word) cpu->regs[rs2]);
|
||
break;
|
||
case MATCH_AMOMINU_D:
|
||
case MATCH_AMOMINU_W:
|
||
tmp = min ((unsigned_word) cpu->regs[rd], (unsigned_word) cpu->regs[rs2]);
|
||
break;
|
||
case MATCH_AMOOR_D:
|
||
case MATCH_AMOOR_W:
|
||
tmp = cpu->regs[rd] | cpu->regs[rs2];
|
||
break;
|
||
case MATCH_AMOSWAP_D:
|
||
case MATCH_AMOSWAP_W:
|
||
tmp = cpu->regs[rs2];
|
||
break;
|
||
case MATCH_AMOXOR_D:
|
||
case MATCH_AMOXOR_W:
|
||
tmp = cpu->regs[rd] ^ cpu->regs[rs2];
|
||
break;
|
||
default:
|
||
TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
|
||
sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
|
||
}
|
||
|
||
if (op->xlen_requirement == 64)
|
||
sim_core_write_unaligned_8 (cpu, cpu->pc, write_map, cpu->regs[rs1], tmp);
|
||
else
|
||
sim_core_write_unaligned_4 (cpu, cpu->pc, write_map, cpu->regs[rs1], tmp);
|
||
|
||
done:
|
||
return pc;
|
||
}
|
||
|
||
static sim_cia
|
||
execute_one (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
|
||
{
|
||
SIM_DESC sd = CPU_STATE (cpu);
|
||
|
||
if (op->xlen_requirement == 32)
|
||
RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name);
|
||
else if (op->xlen_requirement == 64)
|
||
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
|
||
|
||
switch (op->insn_class)
|
||
{
|
||
case INSN_CLASS_A:
|
||
return execute_a (cpu, iw, op);
|
||
case INSN_CLASS_I:
|
||
return execute_i (cpu, iw, op);
|
||
case INSN_CLASS_M:
|
||
return execute_m (cpu, iw, op);
|
||
default:
|
||
TRACE_INSN (cpu, "UNHANDLED EXTENSION: %d", op->insn_class);
|
||
sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
|
||
}
|
||
|
||
return cpu->pc + riscv_insn_length (iw);
|
||
}
|
||
|
||
/* Decode & execute a single instruction. */
|
||
void step_once (SIM_CPU *cpu)
|
||
{
|
||
SIM_DESC sd = CPU_STATE (cpu);
|
||
unsigned_word iw;
|
||
unsigned int len;
|
||
sim_cia pc = cpu->pc;
|
||
const struct riscv_opcode *op;
|
||
int xlen = RISCV_XLEN (cpu);
|
||
|
||
if (TRACE_ANY_P (cpu))
|
||
trace_prefix (sd, cpu, NULL_CIA, pc, TRACE_LINENUM_P (cpu),
|
||
NULL, 0, " "); /* Use a space for gcc warnings. */
|
||
|
||
iw = sim_core_read_aligned_2 (cpu, pc, exec_map, pc);
|
||
|
||
/* Reject non-32-bit opcodes first. */
|
||
len = riscv_insn_length (iw);
|
||
if (len != 4)
|
||
{
|
||
sim_io_printf (sd, "sim: bad insn len %#x @ %#" PRIxTA ": %#" PRIxTW "\n",
|
||
len, pc, iw);
|
||
sim_engine_halt (sd, cpu, NULL, pc, sim_signalled, SIM_SIGILL);
|
||
}
|
||
|
||
iw |= ((unsigned_word) sim_core_read_aligned_2 (
|
||
cpu, pc, exec_map, pc + 2) << 16);
|
||
|
||
TRACE_CORE (cpu, "0x%08" PRIxTW, iw);
|
||
|
||
op = riscv_hash[OP_HASH_IDX (iw)];
|
||
if (!op)
|
||
sim_engine_halt (sd, cpu, NULL, pc, sim_signalled, SIM_SIGILL);
|
||
|
||
/* NB: Same loop logic as riscv_disassemble_insn. */
|
||
for (; op->name; op++)
|
||
{
|
||
/* Does the opcode match? */
|
||
if (! op->match_func (op, iw))
|
||
continue;
|
||
/* Is this a pseudo-instruction and may we print it as such? */
|
||
if (op->pinfo & INSN_ALIAS)
|
||
continue;
|
||
/* Is this instruction restricted to a certain value of XLEN? */
|
||
if (op->xlen_requirement != 0 && op->xlen_requirement != xlen)
|
||
continue;
|
||
|
||
/* It's a match. */
|
||
pc = execute_one (cpu, iw, op);
|
||
break;
|
||
}
|
||
|
||
/* TODO: Handle overflow into high 32 bits. */
|
||
/* TODO: Try to use a common counter and only update on demand (reads). */
|
||
++cpu->csr.cycle;
|
||
++cpu->csr.instret;
|
||
|
||
cpu->pc = pc;
|
||
}
|
||
|
||
/* Return the program counter for this cpu. */
|
||
static sim_cia
|
||
pc_get (sim_cpu *cpu)
|
||
{
|
||
return cpu->pc;
|
||
}
|
||
|
||
/* Set the program counter for this cpu to the new pc value. */
|
||
static void
|
||
pc_set (sim_cpu *cpu, sim_cia pc)
|
||
{
|
||
cpu->pc = pc;
|
||
}
|
||
|
||
static int
|
||
reg_fetch (sim_cpu *cpu, int rn, unsigned char *buf, int len)
|
||
{
|
||
if (len <= 0 || len > sizeof (unsigned_word))
|
||
return -1;
|
||
|
||
switch (rn)
|
||
{
|
||
case SIM_RISCV_ZERO_REGNUM:
|
||
memset (buf, 0, len);
|
||
return len;
|
||
case SIM_RISCV_RA_REGNUM ... SIM_RISCV_T6_REGNUM:
|
||
memcpy (buf, &cpu->regs[rn], len);
|
||
return len;
|
||
case SIM_RISCV_FIRST_FP_REGNUM ... SIM_RISCV_LAST_FP_REGNUM:
|
||
memcpy (buf, &cpu->fpregs[rn - SIM_RISCV_FIRST_FP_REGNUM], len);
|
||
return len;
|
||
case SIM_RISCV_PC_REGNUM:
|
||
memcpy (buf, &cpu->pc, len);
|
||
return len;
|
||
|
||
#define DECLARE_CSR(name, num, ...) \
|
||
case SIM_RISCV_ ## num ## _REGNUM: \
|
||
memcpy (buf, &cpu->csr.name, len); \
|
||
return len;
|
||
#include "opcode/riscv-opc.h"
|
||
#undef DECLARE_CSR
|
||
|
||
default:
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
static int
|
||
reg_store (sim_cpu *cpu, int rn, unsigned char *buf, int len)
|
||
{
|
||
if (len <= 0 || len > sizeof (unsigned_word))
|
||
return -1;
|
||
|
||
switch (rn)
|
||
{
|
||
case SIM_RISCV_ZERO_REGNUM:
|
||
/* Ignore writes. */
|
||
return len;
|
||
case SIM_RISCV_RA_REGNUM ... SIM_RISCV_T6_REGNUM:
|
||
memcpy (&cpu->regs[rn], buf, len);
|
||
return len;
|
||
case SIM_RISCV_FIRST_FP_REGNUM ... SIM_RISCV_LAST_FP_REGNUM:
|
||
memcpy (&cpu->fpregs[rn - SIM_RISCV_FIRST_FP_REGNUM], buf, len);
|
||
return len;
|
||
case SIM_RISCV_PC_REGNUM:
|
||
memcpy (&cpu->pc, buf, len);
|
||
return len;
|
||
|
||
#define DECLARE_CSR(name, num, ...) \
|
||
case SIM_RISCV_ ## num ## _REGNUM: \
|
||
memcpy (&cpu->csr.name, buf, len); \
|
||
return len;
|
||
#include "opcode/riscv-opc.h"
|
||
#undef DECLARE_CSR
|
||
|
||
default:
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
/* Initialize the state for a single cpu. Usuaully this involves clearing all
|
||
registers back to their reset state. Should also hook up the fetch/store
|
||
helper functions too. */
|
||
void
|
||
initialize_cpu (SIM_DESC sd, SIM_CPU *cpu, int mhartid)
|
||
{
|
||
const char *extensions;
|
||
int i;
|
||
|
||
memset (cpu->regs, 0, sizeof (cpu->regs));
|
||
|
||
CPU_PC_FETCH (cpu) = pc_get;
|
||
CPU_PC_STORE (cpu) = pc_set;
|
||
CPU_REG_FETCH (cpu) = reg_fetch;
|
||
CPU_REG_STORE (cpu) = reg_store;
|
||
|
||
if (!riscv_hash[0])
|
||
{
|
||
const struct riscv_opcode *op;
|
||
|
||
for (op = riscv_opcodes; op->name; op++)
|
||
if (!riscv_hash[OP_HASH_IDX (op->match)])
|
||
riscv_hash[OP_HASH_IDX (op->match)] = op;
|
||
}
|
||
|
||
cpu->csr.misa = 0;
|
||
/* RV32 sets this field to 0, and we don't really support RV128 yet. */
|
||
if (RISCV_XLEN (cpu) == 64)
|
||
cpu->csr.misa |= (unsigned64)2 << 62;
|
||
|
||
/* Skip the leading "rv" prefix and the two numbers. */
|
||
extensions = MODEL_NAME (CPU_MODEL (cpu)) + 4;
|
||
for (i = 0; i < 26; ++i)
|
||
{
|
||
char ext = 'A' + i;
|
||
|
||
if (ext == 'X')
|
||
continue;
|
||
else if (strchr (extensions, ext) != NULL)
|
||
{
|
||
if (ext == 'G')
|
||
cpu->csr.misa |= 0x1129; /* G = IMAFD. */
|
||
else
|
||
cpu->csr.misa |= (1 << i);
|
||
}
|
||
}
|
||
|
||
cpu->csr.mimpid = 0x8000;
|
||
cpu->csr.mhartid = mhartid;
|
||
}
|
||
|
||
/* Some utils don't like having a NULL environ. */
|
||
static const char * const simple_env[] = { "HOME=/", "PATH=/bin", NULL };
|
||
|
||
/* Count the number of arguments in an argv. */
|
||
static int
|
||
count_argv (const char * const *argv)
|
||
{
|
||
int i;
|
||
|
||
if (!argv)
|
||
return -1;
|
||
|
||
for (i = 0; argv[i] != NULL; ++i)
|
||
continue;
|
||
return i;
|
||
}
|
||
|
||
void
|
||
initialize_env (SIM_DESC sd, const char * const *argv, const char * const *env)
|
||
{
|
||
SIM_CPU *cpu = STATE_CPU (sd, 0);
|
||
int i;
|
||
int argc, argv_flat;
|
||
int envc, env_flat;
|
||
address_word sp, sp_flat;
|
||
unsigned char null[8] = { 0, 0, 0, 0, 0, 0, 0, 0, };
|
||
|
||
/* Figure out how many bytes the argv strings take up. */
|
||
argc = count_argv (argv);
|
||
if (argc == -1)
|
||
argc = 0;
|
||
argv_flat = argc; /* NUL bytes. */
|
||
for (i = 0; i < argc; ++i)
|
||
argv_flat += strlen (argv[i]);
|
||
|
||
/* Figure out how many bytes the environ strings take up. */
|
||
if (!env)
|
||
env = simple_env;
|
||
envc = count_argv (env);
|
||
env_flat = envc; /* NUL bytes. */
|
||
for (i = 0; i < envc; ++i)
|
||
env_flat += strlen (env[i]);
|
||
|
||
/* Make space for the strings themselves. */
|
||
sp_flat = (DEFAULT_MEM_SIZE - argv_flat - env_flat) & -sizeof (address_word);
|
||
/* Then the pointers to the strings. */
|
||
sp = sp_flat - ((argc + 1 + envc + 1) * sizeof (address_word));
|
||
/* Then the argc. */
|
||
sp -= sizeof (unsigned_word);
|
||
|
||
/* Set up the regs the libgloss crt0 expects. */
|
||
cpu->a0 = argc;
|
||
cpu->sp = sp;
|
||
|
||
/* First push the argc value. */
|
||
sim_write (sd, sp, (void *)&argc, sizeof (unsigned_word));
|
||
sp += sizeof (unsigned_word);
|
||
|
||
/* Then the actual argv strings so we know where to point argv[]. */
|
||
for (i = 0; i < argc; ++i)
|
||
{
|
||
unsigned len = strlen (argv[i]) + 1;
|
||
sim_write (sd, sp_flat, (void *)argv[i], len);
|
||
sim_write (sd, sp, (void *)&sp_flat, sizeof (address_word));
|
||
sp_flat += len;
|
||
sp += sizeof (address_word);
|
||
}
|
||
sim_write (sd, sp, null, sizeof (address_word));
|
||
sp += sizeof (address_word);
|
||
|
||
/* Then the actual env strings so we know where to point env[]. */
|
||
for (i = 0; i < envc; ++i)
|
||
{
|
||
unsigned len = strlen (env[i]) + 1;
|
||
sim_write (sd, sp_flat, (void *)env[i], len);
|
||
sim_write (sd, sp, (void *)&sp_flat, sizeof (address_word));
|
||
sp_flat += len;
|
||
sp += sizeof (address_word);
|
||
}
|
||
}
|