/* RISC-V simulator.
Copyright (C) 2005-2024 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 . */
/* 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
#include
#include "sim-main.h"
#include "sim-signal.h"
#include "sim-syscall.h"
#include "opcode/riscv.h"
#include "sim/sim-riscv.h"
#include "riscv-sim.h"
#define TRACE_REG(cpu, reg) \
TRACE_REGISTER (cpu, "wrote %s = %#" PRIxTW, riscv_gpr_names_abi[reg], \
RISCV_SIM_CPU (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) \
{ \
TRACE_INSN (cpu, "RV32I-only " fmt, ## args); \
sim_engine_halt (CPU_STATE (cpu), cpu, NULL, sim_pc_get (cpu), \
sim_signalled, SIM_SIGILL); \
} \
} while (0)
#define RISCV_ASSERT_RV64(cpu, fmt, args...) \
do { \
if (RISCV_XLEN (cpu) != 64) \
{ \
TRACE_INSN (cpu, "RV64I-only " fmt, ## args); \
sim_engine_halt (CPU_STATE (cpu), cpu, NULL, sim_pc_get (cpu), \
sim_signalled, SIM_SIGILL); \
} \
} while (0)
static INLINE void
store_rd (SIM_CPU *cpu, int rd, unsigned_word val)
{
struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
if (rd)
{
riscv_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)
{
struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
switch (csr)
{
/* These are pseudo registers that modify sub-fields of fcsr. */
case CSR_FRM:
val &= 0x7;
*reg = val;
riscv_cpu->csr.fcsr = (riscv_cpu->csr.fcsr & ~0xe0) | (val << 5);
break;
case CSR_FFLAGS:
val &= 0x1f;
*reg = val;
riscv_cpu->csr.fcsr = (riscv_cpu->csr.fcsr & ~0x1f) | val;
break;
/* Keep the sub-fields in sync. */
case CSR_FCSR:
*reg = val;
riscv_cpu->csr.frm = (val >> 5) & 0x7;
riscv_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);
ATTRIBUTE_FALLTHROUGH;
/* 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)
{
uint32_t sign = (val & 0x80000000) ? ~(0xfffffffful >> shift) : 0;
return (val >> shift) | sign;
}
static inline unsigned_word
ashiftrt64 (unsigned_word val, unsigned_word shift)
{
uint64_t 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);
struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (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 ((uint64_t) 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 = riscv_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,
riscv_cpu->regs[rs1],
rs2, rs2_name, (int) sizeof (unsigned_word) * 2,
riscv_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, riscv_cpu->regs[rs1] + riscv_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 (riscv_cpu->regs[rs1] + riscv_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, riscv_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 (riscv_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, riscv_cpu->regs[rs1] & riscv_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, riscv_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, riscv_cpu->regs[rs1] | riscv_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, riscv_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, riscv_cpu->regs[rs1] ^ riscv_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, riscv_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, riscv_cpu->regs[rs1] - riscv_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 (riscv_cpu->regs[rs1] - riscv_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,
riscv_cpu->regs[rs1] << (riscv_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 (
(uint32_t) riscv_cpu->regs[rs1] << (riscv_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, riscv_cpu->pc, sim_signalled,
SIM_SIGILL);
store_rd (cpu, rd, riscv_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 ((uint32_t) riscv_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,
riscv_cpu->regs[rs1] >> (riscv_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 (
(uint32_t) riscv_cpu->regs[rs1] >> (riscv_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, riscv_cpu->pc, sim_signalled,
SIM_SIGILL);
store_rd (cpu, rd, riscv_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 ((uint32_t) riscv_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 (riscv_cpu->regs[rs1], riscv_cpu->regs[rs2] & 0x1f);
else
tmp = ashiftrt64 (riscv_cpu->regs[rs1], riscv_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 ((int32_t) riscv_cpu->regs[rs1],
riscv_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, riscv_cpu->pc, sim_signalled,
SIM_SIGILL);
tmp = ashiftrt (riscv_cpu->regs[rs1], shamt_imm);
}
else
tmp = ashiftrt64 (riscv_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 ((int32_t) riscv_cpu->regs[rs1], shamt_imm)));
break;
case MATCH_SLT:
TRACE_INSN (cpu, "slt");
store_rd (cpu, rd,
!!((signed_word) riscv_cpu->regs[rs1] <
(signed_word) riscv_cpu->regs[rs2]));
break;
case MATCH_SLTU:
TRACE_INSN (cpu, "sltu");
store_rd (cpu, rd, !!((unsigned_word) riscv_cpu->regs[rs1] <
(unsigned_word) riscv_cpu->regs[rs2]));
break;
case MATCH_SLTI:
TRACE_INSN (cpu, "slti");
store_rd (cpu, rd, !!((signed_word) riscv_cpu->regs[rs1] <
(signed_word) i_imm));
break;
case MATCH_SLTIU:
TRACE_INSN (cpu, "sltiu");
store_rd (cpu, rd, !!((unsigned_word) riscv_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, riscv_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 (riscv_cpu->regs[rs1] == riscv_cpu->regs[rs2])
{
pc = riscv_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) riscv_cpu->regs[rs1] <
(signed_word) riscv_cpu->regs[rs2])
{
pc = riscv_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) riscv_cpu->regs[rs1] <
(unsigned_word) riscv_cpu->regs[rs2])
{
pc = riscv_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) riscv_cpu->regs[rs1] >=
(signed_word) riscv_cpu->regs[rs2])
{
pc = riscv_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) riscv_cpu->regs[rs1] >=
(unsigned_word) riscv_cpu->regs[rs2])
{
pc = riscv_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 (riscv_cpu->regs[rs1] != riscv_cpu->regs[rs2])
{
pc = riscv_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, riscv_cpu->pc + 4);
pc = riscv_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);
pc = riscv_cpu->regs[rs1] + i_imm;
store_rd (cpu, rd, riscv_cpu->pc + 4);
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, riscv_cpu->pc, read_map,
riscv_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, riscv_cpu->pc, read_map,
riscv_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, riscv_cpu->pc, read_map,
riscv_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, riscv_cpu->pc, read_map,
riscv_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, riscv_cpu->pc, read_map,
riscv_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, riscv_cpu->pc, read_map,
riscv_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, riscv_cpu->pc, read_map,
riscv_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, riscv_cpu->pc, write_map,
riscv_cpu->regs[rs1] + s_imm,
riscv_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, riscv_cpu->pc, write_map,
riscv_cpu->regs[rs1] + s_imm,
riscv_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, riscv_cpu->pc, write_map,
riscv_cpu->regs[rs1] + s_imm,
riscv_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, riscv_cpu->pc, write_map,
riscv_cpu->regs[rs1] + s_imm,
riscv_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, &riscv_cpu->csr.name)); \
store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
riscv_cpu->csr.name & !riscv_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, &riscv_cpu->csr.name)); \
store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
riscv_cpu->csr.name | riscv_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, &riscv_cpu->csr.name)); \
store_csr (cpu, #name, num, &riscv_cpu->csr.name, \
riscv_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, &riscv_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, &riscv_cpu->csr.cycleh));
break;
case MATCH_RDINSTRET:
TRACE_INSN (cpu, "rdinstret %s;", rd_name);
store_rd (cpu, rd,
fetch_csr (cpu, "instret", CSR_INSTRET,
&riscv_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,
&riscv_cpu->csr.instreth));
break;
case MATCH_RDTIME:
TRACE_INSN (cpu, "rdtime %s;", rd_name);
store_rd (cpu, rd,
fetch_csr (cpu, "time", CSR_TIME, &riscv_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, &riscv_cpu->csr.timeh));
break;
case MATCH_FENCE:
TRACE_INSN (cpu, "fence;");
break;
case MATCH_FENCE_I:
TRACE_INSN (cpu, "fence.i;");
break;
case MATCH_EBREAK:
TRACE_INSN (cpu, "ebreak;");
/* GDB expects us to step over EBREAK. */
sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc + 4, sim_stopped,
SIM_SIGTRAP);
break;
case MATCH_ECALL:
TRACE_INSN (cpu, "ecall;");
riscv_cpu->a0 = sim_syscall (cpu, riscv_cpu->a7, riscv_cpu->a0,
riscv_cpu->a1, riscv_cpu->a2, riscv_cpu->a3);
break;
default:
TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled, SIM_SIGILL);
}
return pc;
}
static uint64_t
mulhu (uint64_t a, uint64_t 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 uint64_t
mulh (int64_t a, int64_t 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 uint64_t
mulhsu (int64_t a, uint64_t 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)
{
struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
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 = riscv_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 (riscv_cpu->regs[rs1] == dividend_max && riscv_cpu->regs[rs2] == -1)
tmp = dividend_max;
else if (riscv_cpu->regs[rs2])
tmp = (signed_word) riscv_cpu->regs[rs1] /
(signed_word) riscv_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 (riscv_cpu->regs[rs2]) == -1)
tmp = 1 << 31;
else if (EXTEND32 (riscv_cpu->regs[rs2]))
tmp = EXTEND32 (riscv_cpu->regs[rs1]) / EXTEND32 (riscv_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 (riscv_cpu->regs[rs2])
store_rd (cpu, rd, (unsigned_word) riscv_cpu->regs[rs1]
/ (unsigned_word) riscv_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 ((uint32_t) riscv_cpu->regs[rs2])
tmp = (uint32_t) riscv_cpu->regs[rs1] / (uint32_t) riscv_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, riscv_cpu->regs[rs1] * riscv_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 ((int32_t) riscv_cpu->regs[rs1]
* (int32_t) riscv_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,
((int64_t)(signed_word) riscv_cpu->regs[rs1]
* (int64_t)(signed_word) riscv_cpu->regs[rs2]) >> 32);
else
store_rd (cpu, rd, mulh (riscv_cpu->regs[rs1], riscv_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, ((uint64_t)riscv_cpu->regs[rs1]
* (uint64_t)riscv_cpu->regs[rs2]) >> 32);
else
store_rd (cpu, rd, mulhu (riscv_cpu->regs[rs1], riscv_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, ((int64_t)(signed_word) riscv_cpu->regs[rs1]
* (uint64_t)riscv_cpu->regs[rs2]) >> 32);
else
store_rd (cpu, rd, mulhsu (riscv_cpu->regs[rs1], riscv_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 (riscv_cpu->regs[rs1] == dividend_max && riscv_cpu->regs[rs2] == -1)
tmp = 0;
else if (riscv_cpu->regs[rs2])
tmp = (signed_word) riscv_cpu->regs[rs1]
% (signed_word) riscv_cpu->regs[rs2];
else
tmp = riscv_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 (riscv_cpu->regs[rs2]) == -1)
tmp = 0;
else if (EXTEND32 (riscv_cpu->regs[rs2]))
tmp = EXTEND32 (riscv_cpu->regs[rs1]) % EXTEND32 (riscv_cpu->regs[rs2]);
else
tmp = riscv_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 (riscv_cpu->regs[rs2])
store_rd (cpu, rd, riscv_cpu->regs[rs1] % riscv_cpu->regs[rs2]);
else
store_rd (cpu, rd, riscv_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 ((uint32_t) riscv_cpu->regs[rs2])
tmp = (uint32_t) riscv_cpu->regs[rs1] % (uint32_t) riscv_cpu->regs[rs2];
else
tmp = riscv_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, riscv_cpu->pc, sim_signalled, SIM_SIGILL);
}
return pc;
}
static sim_cia
execute_a (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
{
struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
SIM_DESC sd = CPU_STATE (cpu);
struct riscv_sim_state *state = RISCV_SIM_STATE (sd);
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 = riscv_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, riscv_cpu->pc, read_map,
riscv_cpu->regs[rs1]));
/* Walk the reservation list to find an existing match. */
amo_curr = state->amo_reserved_list;
while (amo_curr)
{
if (amo_curr->addr == riscv_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 = riscv_cpu->regs[rs1];
amo_curr->next = state->amo_reserved_list;
state->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 = state->amo_reserved_list;
while (amo_curr)
{
if (amo_curr->addr == riscv_cpu->regs[rs1])
{
/* We found a reservation, so operate it. */
sim_core_write_unaligned_4 (cpu, riscv_cpu->pc, write_map,
riscv_cpu->regs[rs1],
riscv_cpu->regs[rs2]);
store_rd (cpu, rd, 0);
if (amo_curr == state->amo_reserved_list)
state->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, riscv_cpu->pc, read_map,
riscv_cpu->regs[rs1]);
else
tmp = EXTEND32 (sim_core_read_unaligned_4 (cpu, riscv_cpu->pc, read_map,
riscv_cpu->regs[rs1]));
store_rd (cpu, rd, tmp);
switch (op->match)
{
case MATCH_AMOADD_D:
case MATCH_AMOADD_W:
tmp = riscv_cpu->regs[rd] + riscv_cpu->regs[rs2];
break;
case MATCH_AMOAND_D:
case MATCH_AMOAND_W:
tmp = riscv_cpu->regs[rd] & riscv_cpu->regs[rs2];
break;
case MATCH_AMOMAX_D:
case MATCH_AMOMAX_W:
tmp = max ((signed_word) riscv_cpu->regs[rd],
(signed_word) riscv_cpu->regs[rs2]);
break;
case MATCH_AMOMAXU_D:
case MATCH_AMOMAXU_W:
tmp = max ((unsigned_word) riscv_cpu->regs[rd],
(unsigned_word) riscv_cpu->regs[rs2]);
break;
case MATCH_AMOMIN_D:
case MATCH_AMOMIN_W:
tmp = min ((signed_word) riscv_cpu->regs[rd],
(signed_word) riscv_cpu->regs[rs2]);
break;
case MATCH_AMOMINU_D:
case MATCH_AMOMINU_W:
tmp = min ((unsigned_word) riscv_cpu->regs[rd],
(unsigned_word) riscv_cpu->regs[rs2]);
break;
case MATCH_AMOOR_D:
case MATCH_AMOOR_W:
tmp = riscv_cpu->regs[rd] | riscv_cpu->regs[rs2];
break;
case MATCH_AMOSWAP_D:
case MATCH_AMOSWAP_W:
tmp = riscv_cpu->regs[rs2];
break;
case MATCH_AMOXOR_D:
case MATCH_AMOXOR_W:
tmp = riscv_cpu->regs[rd] ^ riscv_cpu->regs[rs2];
break;
default:
TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled, SIM_SIGILL);
}
if (op->xlen_requirement == 64)
sim_core_write_unaligned_8 (cpu, riscv_cpu->pc, write_map,
riscv_cpu->regs[rs1], tmp);
else
sim_core_write_unaligned_4 (cpu, riscv_cpu->pc, write_map,
riscv_cpu->regs[rs1], tmp);
done:
return pc;
}
static sim_cia
execute_c (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
{
SIM_DESC sd = CPU_STATE (cpu);
struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
int rd = (iw >> OP_SH_RD) & OP_MASK_RD;
int rs1_c = ((iw >> OP_SH_CRS1S) & OP_MASK_CRS1S) + 8;
int rs2 = (iw >> OP_SH_CRS2) & OP_MASK_CRS2;
int rs2_c = ((iw >> OP_SH_CRS2S) & OP_MASK_CRS2S) + 8;
const char *rd_name = riscv_gpr_names_abi[rd];
const char *rs1_c_name = riscv_gpr_names_abi[rs1_c];
const char *rs2_name = riscv_gpr_names_abi[rs2];
const char *rs2_c_name = riscv_gpr_names_abi[rs2_c];
signed_word imm;
unsigned_word tmp;
sim_cia pc = riscv_cpu->pc + 2;
switch (op->match)
{
case MATCH_C_JR | MATCH_C_MV:
switch (op->mask)
{
case MASK_C_MV:
TRACE_INSN (cpu, "c.mv %s, %s; // %s = %s",
rd_name, rs2_name, rd_name, rs2_name);
store_rd (cpu, rd, riscv_cpu->regs[rs2]);
break;
case MASK_C_JR:
TRACE_INSN (cpu, "c.jr %s;",
rd_name);
pc = riscv_cpu->regs[rd];
TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
break;
}
break;
case MATCH_C_J:
imm = EXTRACT_CJTYPE_IMM (iw);
TRACE_INSN (cpu, "c.j %" PRIxTW,
imm);
pc = riscv_cpu->pc + imm;
TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
break;
case MATCH_C_JAL | MATCH_C_ADDIW:
/* JAL and ADDIW have the same mask but are only available on RV64 or
RV32 respectively. */
if (RISCV_XLEN (cpu) == 64)
{
imm = EXTRACT_CJTYPE_IMM (iw);
TRACE_INSN (cpu, "c.jal %" PRIxTW,
imm);
store_rd (cpu, SIM_RISCV_RA_REGNUM, riscv_cpu->pc + 2);
pc = riscv_cpu->pc + imm;
TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
}
else if (RISCV_XLEN (cpu) == 32)
{
imm = EXTRACT_CITYPE_IMM (iw);
TRACE_INSN (cpu, "c.addiw %s, %s, %#" PRIxTW "; // %s += %#" PRIxTW,
rd_name, rd_name, imm, rd_name, imm);
store_rd (cpu, rd, EXTEND32 (riscv_cpu->regs[rd] + imm));
}
else
{
TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled,
SIM_SIGILL);
}
break;
case MATCH_C_JALR | MATCH_C_ADD | MATCH_C_EBREAK:
switch (op->mask)
{
case MASK_C_ADD:
TRACE_INSN (cpu, "c.add %s, %s; // %s += %s",
rd_name, rs2_name, rd_name, rs2_name);
store_rd (cpu, rd, riscv_cpu->regs[rd] + riscv_cpu->regs[rs2]);
break;
case MASK_C_JALR:
TRACE_INSN (cpu, "c.jalr %s, %s;",
riscv_gpr_names_abi[SIM_RISCV_RA_REGNUM], rd_name);
store_rd (cpu, SIM_RISCV_RA_REGNUM, riscv_cpu->pc + 2);
pc = riscv_cpu->regs[rd];
TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
break;
case MASK_C_EBREAK:
TRACE_INSN (cpu, "ebreak");
sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_stopped,
SIM_SIGTRAP);
}
break;
case MATCH_C_BEQZ:
imm = EXTRACT_CBTYPE_IMM (iw);
TRACE_INSN (cpu, "c.beqz %s, %#" PRIxTW "; "
"// if (%s == 0) goto %#" PRIxTW,
rs1_c_name, imm, rs1_c_name, riscv_cpu->pc + imm);
if (riscv_cpu->regs[rs1_c] == riscv_cpu->regs[0])
{
pc = riscv_cpu->pc + imm;
TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
}
break;
case MATCH_C_BNEZ:
imm = EXTRACT_CBTYPE_IMM (iw);
TRACE_INSN (cpu, "c.bnez %s, %#" PRIxTW "; "
"// if (%s != 0) goto %#" PRIxTW,
rs1_c_name, imm, rs1_c_name, riscv_cpu->pc + imm);
if (riscv_cpu->regs[rs1_c] != riscv_cpu->regs[0])
{
pc = riscv_cpu->pc + imm;
TRACE_BRANCH (cpu, "to %#" PRIxTW, pc);
}
break;
case MATCH_C_LWSP:
imm = EXTRACT_CITYPE_LWSP_IMM (iw);
TRACE_INSN (cpu, "c.lwsp %s, %" PRIiTW "(sp);",
rd_name, imm);
store_rd (cpu, rd, EXTEND32 (
sim_core_read_unaligned_4 (cpu, riscv_cpu->pc, read_map,
riscv_cpu->regs[SIM_RISCV_SP_REGNUM]
+ imm)));
break;
case MATCH_C_LW:
imm = EXTRACT_CLTYPE_LW_IMM (iw);
TRACE_INSN (cpu, "c.lw %s, %" PRIiTW "(%s);",
rs2_c_name, imm, rs1_c_name);
store_rd (cpu, rs2_c, EXTEND32 (
sim_core_read_unaligned_4 (cpu, riscv_cpu->pc, read_map,
riscv_cpu->regs[rs1_c] + imm)));
break;
case MATCH_C_SWSP:
imm = EXTRACT_CSSTYPE_SWSP_IMM (iw);
TRACE_INSN (cpu, "c.swsp %s, %" PRIiTW "(sp);",
rs2_name, imm);
sim_core_write_unaligned_4 (cpu, riscv_cpu->pc, write_map,
riscv_cpu->regs[SIM_RISCV_SP_REGNUM] + imm,
riscv_cpu->regs[rs2]);
break;
case MATCH_C_SW:
imm = EXTRACT_CLTYPE_LW_IMM (iw);
TRACE_INSN (cpu, "c.sw %s, %" PRIiTW "(%s);",
rs2_c_name, imm, rs1_c_name);
sim_core_write_unaligned_4 (cpu, riscv_cpu->pc, write_map,
riscv_cpu->regs[rs1_c] + (imm),
riscv_cpu->regs[rs2_c]);
break;
case MATCH_C_ADDI:
imm = EXTRACT_CITYPE_IMM (iw);
TRACE_INSN (cpu, "c.addi %s, %s, %#" PRIxTW "; // %s += %#" PRIxTW,
rd_name, rd_name, imm, rd_name, imm);
store_rd (cpu, rd, riscv_cpu->regs[rd] + imm);
break;
case MATCH_C_LUI:
imm = EXTRACT_CITYPE_LUI_IMM (iw);
TRACE_INSN (cpu, "c.lui %s, %#" PRIxTW ";",
rd_name, imm);
store_rd (cpu, rd, imm);
break;
case MATCH_C_LI:
imm = EXTRACT_CITYPE_IMM (iw);
TRACE_INSN (cpu, "c.li %s, %#" PRIxTW "; // %s = %#" PRIxTW,
rd_name, imm, rd_name, imm);
store_rd (cpu, rd, imm);
break;
case MATCH_C_ADDI4SPN:
imm = EXTRACT_CIWTYPE_ADDI4SPN_IMM (iw);
TRACE_INSN (cpu, "c.addi4spn %s, %" PRIiTW "; // %s = sp + %" PRIiTW,
rs2_c_name, imm, rs2_c_name, imm);
store_rd (cpu, rs2_c, riscv_cpu->regs[SIM_RISCV_SP_REGNUM] + (imm));
break;
case MATCH_C_ADDI16SP:
imm = EXTRACT_CITYPE_ADDI16SP_IMM (iw);
TRACE_INSN (cpu, "c.addi16sp %s, %" PRIiTW "; // %s = sp + %" PRIiTW,
rd_name, imm, rd_name, imm);
store_rd (cpu, rd, riscv_cpu->regs[SIM_RISCV_SP_REGNUM] + imm);
break;
case MATCH_C_SUB:
TRACE_INSN (cpu, "c.sub %s, %s; // %s = %s - %s",
rs1_c_name, rs2_c_name, rs1_c_name, rs1_c_name, rs2_c_name);
store_rd (cpu, rs1_c, riscv_cpu->regs[rs1_c] - riscv_cpu->regs[rs2_c]);
break;
case MATCH_C_AND:
TRACE_INSN (cpu, "c.and %s, %s; // %s = %s & %s",
rs1_c_name, rs2_c_name, rs1_c_name, rs1_c_name, rs2_c_name);
store_rd (cpu, rs1_c, riscv_cpu->regs[rs1_c] & riscv_cpu->regs[rs2_c]);
break;
case MATCH_C_OR:
TRACE_INSN (cpu, "c.or %s, %s; // %s = %s | %s",
rs1_c_name, rs2_c_name, rs1_c_name, rs1_c_name, rs2_c_name);
store_rd (cpu, rs1_c, riscv_cpu->regs[rs1_c] | riscv_cpu->regs[rs2_c]);
break;
case MATCH_C_XOR:
TRACE_INSN (cpu, "c.xor %s, %s; // %s = %s ^ %s",
rs1_c_name, rs2_c_name, rs1_c_name, rs1_c_name, rs2_c_name);
store_rd (cpu, rs1_c, riscv_cpu->regs[rs1_c] ^ riscv_cpu->regs[rs2_c]);
break;
case MATCH_C_SLLI | MATCH_C_SLLI64:
if (op->mask == MASK_C_SLLI64)
{
/* Reserved for custom use. */
TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled,
SIM_SIGILL);
break;
}
imm = EXTRACT_CITYPE_IMM (iw);
TRACE_INSN (cpu, "c.slli %s, %" PRIiTW "; // %s = %s << %#" PRIxTW,
rd_name, imm, rd_name, rd_name, imm);
store_rd (cpu, rd, riscv_cpu->regs[rd] << imm);
break;
case MATCH_C_SRLI | MATCH_C_SRLI64:
if (op->mask == MASK_C_SRLI64)
{
/* Reserved for custom use. */
TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled,
SIM_SIGILL);
break;
}
imm = EXTRACT_CITYPE_IMM (iw);
TRACE_INSN (cpu, "c.srli %s, %" PRIiTW "; // %s = %s >> %#" PRIxTW,
rs1_c_name, imm, rs1_c_name, rs1_c_name, imm);
if (RISCV_XLEN (cpu) == 32)
store_rd (cpu, rs1_c,
EXTEND32 ((uint32_t) riscv_cpu->regs[rs1_c] >> imm));
else
store_rd (cpu, rs1_c, riscv_cpu->regs[rs1_c] >> imm);
break;
case MATCH_C_SRAI | MATCH_C_SRAI64:
if (op->mask == MASK_C_SRAI64)
{
/* Reserved for custom use. */
TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled,
SIM_SIGILL);
break;
}
imm = EXTRACT_CITYPE_IMM (iw);
TRACE_INSN (cpu, "c.srai %s, %" PRIiTW "; // %s = %s >> %#" PRIxTW,
rs1_c_name, imm, rs1_c_name, rs1_c_name, imm);
if (RISCV_XLEN (cpu) == 32)
{
if (imm > 0x1f)
sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled,
SIM_SIGILL);
tmp = ashiftrt (riscv_cpu->regs[rs1_c], imm);
}
else
tmp = ashiftrt64 (riscv_cpu->regs[rs1_c], imm);
store_rd (cpu, rd, tmp);
break;
case MATCH_C_ANDI:
imm = EXTRACT_CITYPE_IMM (iw);
TRACE_INSN (cpu, "c.andi %s, %" PRIiTW "; // %s = %s & %#" PRIxTW,
rs1_c_name, imm, rs1_c_name, rs1_c_name, imm);
store_rd (cpu, rs1_c, riscv_cpu->regs[rs1_c] & imm);
break;
case MATCH_C_ADDW:
TRACE_INSN (cpu, "c.addw %s, %s; // %s = %s + %s",
rs1_c_name, rs2_c_name, rs1_c_name, rs1_c_name, rs2_c_name);
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
store_rd (cpu, rs1_c,
EXTEND32 (riscv_cpu->regs[rs1_c] + riscv_cpu->regs[rs2_c]));
break;
case MATCH_C_SUBW:
TRACE_INSN (cpu, "c.subw %s, %s; // %s = %s - %s",
rs1_c_name, rs2_c_name, rs1_c_name, rs1_c_name, rs2_c_name);
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
store_rd (cpu, rs1_c,
EXTEND32 (riscv_cpu->regs[rs1_c] - riscv_cpu->regs[rs2_c]));
break;
case MATCH_C_LDSP:
imm = EXTRACT_CITYPE_LDSP_IMM (iw);
TRACE_INSN (cpu, "c.ldsp %s, %" PRIiTW "(sp);",
rd_name, imm);
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
store_rd (cpu, rd,
sim_core_read_unaligned_8 (cpu, riscv_cpu->pc, read_map,
riscv_cpu->regs[SIM_RISCV_SP_REGNUM]
+ imm));
break;
case MATCH_C_LD:
imm = EXTRACT_CLTYPE_LD_IMM (iw);
TRACE_INSN (cpu, "c.ld %s, %" PRIiTW "(%s);",
rs1_c_name, imm, rs2_c_name);
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
store_rd (cpu, rs2_c,
sim_core_read_unaligned_8 (cpu, riscv_cpu->pc, read_map,
riscv_cpu->regs[rs1_c] + imm));
break;
case MATCH_C_SDSP:
imm = EXTRACT_CSSTYPE_SDSP_IMM (iw);
TRACE_INSN (cpu, "c.sdsp %s, %" PRIiTW "(sp);",
rs2_name, imm);
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
sim_core_write_unaligned_8 (cpu, riscv_cpu->pc, write_map,
riscv_cpu->regs[SIM_RISCV_SP_REGNUM] + imm,
riscv_cpu->regs[rs2]);
break;
case MATCH_C_SD:
imm = EXTRACT_CLTYPE_LD_IMM (iw);
TRACE_INSN (cpu, "c.sd %s, %" PRIiTW "(%s);",
rs2_c_name, imm, rs1_c_name);
RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
sim_core_write_unaligned_8 (cpu, riscv_cpu->pc, write_map,
riscv_cpu->regs[rs1_c] + imm,
riscv_cpu->regs[rs2_c]);
break;
default:
TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled,
SIM_SIGILL);
}
return pc;
}
static sim_cia
execute_one (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
{
struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
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_C:
/* Check whether model with C extension is selected. */
if (riscv_cpu->csr.misa & 4)
return execute_c (cpu, iw, op);
else
{
TRACE_INSN (cpu, "UNHANDLED EXTENSION: %d", op->insn_class);
sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled,
SIM_SIGILL);
}
case INSN_CLASS_I:
return execute_i (cpu, iw, op);
case INSN_CLASS_M:
case INSN_CLASS_ZMMUL:
return execute_m (cpu, iw, op);
default:
TRACE_INSN (cpu, "UNHANDLED EXTENSION: %d", op->insn_class);
sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled, SIM_SIGILL);
}
return riscv_cpu->pc + riscv_insn_length (iw);
}
/* Decode & execute a single instruction. */
void step_once (SIM_CPU *cpu)
{
struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
SIM_DESC sd = CPU_STATE (cpu);
unsigned_word iw;
unsigned int len;
sim_cia pc = riscv_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);
len = riscv_insn_length (iw);
if (len != 4 && len != 2)
{
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);
}
if (len == 4)
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->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). */
++riscv_cpu->csr.cycle;
++riscv_cpu->csr.instret;
riscv_cpu->pc = pc;
}
/* Return the program counter for this cpu. */
static sim_cia
pc_get (sim_cpu *cpu)
{
struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
return riscv_cpu->pc;
}
/* Set the program counter for this cpu to the new pc value. */
static void
pc_set (sim_cpu *cpu, sim_cia pc)
{
struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
riscv_cpu->pc = pc;
}
static int
reg_fetch (sim_cpu *cpu, int rn, void *buf, int len)
{
struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
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, &riscv_cpu->regs[rn], len);
return len;
case SIM_RISCV_FIRST_FP_REGNUM ... SIM_RISCV_LAST_FP_REGNUM:
memcpy (buf, &riscv_cpu->fpregs[rn - SIM_RISCV_FIRST_FP_REGNUM], len);
return len;
case SIM_RISCV_PC_REGNUM:
memcpy (buf, &riscv_cpu->pc, len);
return len;
#define DECLARE_CSR(name, num, ...) \
case SIM_RISCV_ ## num ## _REGNUM: \
memcpy (buf, &riscv_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, const void *buf, int len)
{
struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
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 (&riscv_cpu->regs[rn], buf, len);
return len;
case SIM_RISCV_FIRST_FP_REGNUM ... SIM_RISCV_LAST_FP_REGNUM:
memcpy (&riscv_cpu->fpregs[rn - SIM_RISCV_FIRST_FP_REGNUM], buf, len);
return len;
case SIM_RISCV_PC_REGNUM:
memcpy (&riscv_cpu->pc, buf, len);
return len;
#define DECLARE_CSR(name, num, ...) \
case SIM_RISCV_ ## num ## _REGNUM: \
memcpy (&riscv_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)
{
struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
const char *extensions;
int i;
memset (riscv_cpu->regs, 0, sizeof (riscv_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;
}
riscv_cpu->csr.misa = 0;
/* RV32 sets this field to 0, and we don't really support RV128 yet. */
if (RISCV_XLEN (cpu) == 64)
riscv_cpu->csr.misa |= (uint64_t)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')
riscv_cpu->csr.misa |= 0x1129; /* G = IMAFD. */
else
riscv_cpu->csr.misa |= (1 << i);
}
}
riscv_cpu->csr.mimpid = 0x8000;
riscv_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);
struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
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. */
riscv_cpu->a0 = argc;
riscv_cpu->sp = sp;
/* First push the argc value. */
sim_write (sd, sp, &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, argv[i], len);
sim_write (sd, sp, &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, env[i], len);
sim_write (sd, sp, &sp_flat, sizeof (address_word));
sp_flat += len;
sp += sizeof (address_word);
}
}