mirror of
https://github.com/riscv-software-src/riscv-isa-sim.git
synced 2025-10-14 10:38:57 +08:00
307 lines
9.9 KiB
C++
307 lines
9.9 KiB
C++
#ifndef _RISCV_TRIGGERS_H
|
|
#define _RISCV_TRIGGERS_H
|
|
|
|
#include <vector>
|
|
#include <optional>
|
|
|
|
#include "decode.h"
|
|
|
|
namespace triggers {
|
|
|
|
typedef enum {
|
|
OPERATION_EXECUTE,
|
|
OPERATION_STORE,
|
|
OPERATION_LOAD,
|
|
} operation_t;
|
|
|
|
typedef enum
|
|
{
|
|
ACTION_DEBUG_EXCEPTION = MCONTROL_ACTION_DEBUG_EXCEPTION,
|
|
ACTION_DEBUG_MODE = MCONTROL_ACTION_DEBUG_MODE,
|
|
ACTION_TRACE_START = MCONTROL_ACTION_TRACE_START,
|
|
ACTION_TRACE_STOP = MCONTROL_ACTION_TRACE_STOP,
|
|
ACTION_TRACE_EMIT = MCONTROL_ACTION_TRACE_EMIT,
|
|
ACTION_MAXVAL = MCONTROL_ACTION_TRACE_EMIT
|
|
} action_t;
|
|
|
|
typedef enum {
|
|
TIMING_BEFORE = 0,
|
|
TIMING_AFTER = 1
|
|
} timing_t;
|
|
|
|
typedef enum {
|
|
SSELECT_IGNORE = 0,
|
|
SSELECT_SCONTEXT = 1,
|
|
SSELECT_ASID = 2,
|
|
SSELECT_MAXVAL = 2
|
|
} sselect_t;
|
|
|
|
typedef enum {
|
|
MHSELECT_MODE_IGNORE,
|
|
MHSELECT_MODE_MCONTEXT,
|
|
MHSELECT_MODE_VMID,
|
|
} mhselect_mode_t;
|
|
|
|
struct match_result_t {
|
|
match_result_t(const timing_t t=TIMING_BEFORE, const action_t a=ACTION_DEBUG_EXCEPTION) {
|
|
timing = t;
|
|
action = a;
|
|
}
|
|
timing_t timing;
|
|
action_t action;
|
|
};
|
|
|
|
typedef enum {
|
|
HIT_FALSE = 0,
|
|
HIT_BEFORE = 1,
|
|
HIT_AFTER = 2,
|
|
HIT_IMMEDIATELY_AFTER = 3
|
|
} hit_t;
|
|
|
|
class matched_t
|
|
{
|
|
public:
|
|
matched_t(triggers::operation_t operation, reg_t address, action_t action, bool gva) :
|
|
operation(operation), address(address), action(action), gva(gva) {}
|
|
|
|
triggers::operation_t operation;
|
|
reg_t address;
|
|
action_t action;
|
|
bool gva;
|
|
};
|
|
|
|
class trigger_t {
|
|
public:
|
|
virtual ~trigger_t() {};
|
|
|
|
virtual reg_t tdata1_read(const processor_t * const proc) const noexcept = 0;
|
|
virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept = 0;
|
|
reg_t tdata2_read(const processor_t * const proc) const noexcept;
|
|
void tdata2_write(processor_t * const proc, const reg_t val) noexcept;
|
|
reg_t tdata3_read(const processor_t * const proc) const noexcept;
|
|
void tdata3_write(processor_t * const proc, const reg_t val) noexcept;
|
|
|
|
virtual bool get_dmode() const = 0;
|
|
virtual bool get_chain() const { return false; }
|
|
virtual bool get_execute() const { return false; }
|
|
virtual bool get_store() const { return false; }
|
|
virtual bool get_load() const { return false; }
|
|
virtual action_t get_action() const { return ACTION_DEBUG_EXCEPTION; }
|
|
virtual bool icount_check_needed() const { return false; }
|
|
virtual void stash_read_values() {}
|
|
|
|
virtual std::optional<match_result_t> detect_memory_access_match(processor_t UNUSED * const proc,
|
|
operation_t UNUSED operation, reg_t UNUSED address, std::optional<reg_t> UNUSED data) noexcept { return std::nullopt; }
|
|
virtual std::optional<match_result_t> detect_icount_fire(processor_t UNUSED * const proc) { return std::nullopt; }
|
|
virtual void detect_icount_decrement(processor_t UNUSED * const proc) {}
|
|
virtual std::optional<match_result_t> detect_trap_match(processor_t UNUSED * const proc, const trap_t UNUSED & t) noexcept { return std::nullopt; }
|
|
|
|
protected:
|
|
static action_t legalize_action(reg_t val, reg_t action_mask, reg_t dmode_mask) noexcept;
|
|
bool common_match(processor_t * const proc, bool use_prev_prv = false) const noexcept;
|
|
reg_t tdata2;
|
|
|
|
bool vs = false;
|
|
bool vu = false;
|
|
bool m = false;
|
|
bool s = false;
|
|
bool u = false;
|
|
|
|
private:
|
|
unsigned legalize_mhselect(bool h_enabled) const noexcept;
|
|
bool mode_match(reg_t prv, bool v) const noexcept;
|
|
bool textra_match(processor_t * const proc) const noexcept;
|
|
|
|
struct mhselect_interpretation {
|
|
const unsigned mhselect;
|
|
const mhselect_mode_t mode;
|
|
const std::optional<bool> shift_mhvalue;
|
|
unsigned compare_val(const unsigned mhvalue) const {
|
|
return shift_mhvalue.value() ? (mhvalue << 1 | mhselect >> 2) : mhvalue;
|
|
};
|
|
};
|
|
|
|
mhselect_interpretation interpret_mhselect(bool h_enabled) const noexcept {
|
|
static unsigned warlize_if_h[8] = { 0, 1, 2, 0, 4, 5, 6, 4 }; // 3,7 downgrade
|
|
static unsigned warlize_no_h[8] = { 0, 0, 0, 0, 4, 4, 4, 4 }; // only 0,4 legal
|
|
static std::optional<mhselect_interpretation> table[8] = {
|
|
mhselect_interpretation{ 0, MHSELECT_MODE_IGNORE, std::nullopt },
|
|
mhselect_interpretation{ 1, MHSELECT_MODE_MCONTEXT, true },
|
|
mhselect_interpretation{ 2, MHSELECT_MODE_VMID, true },
|
|
std::nullopt,
|
|
mhselect_interpretation{ 4, MHSELECT_MODE_MCONTEXT, false },
|
|
mhselect_interpretation{ 5, MHSELECT_MODE_MCONTEXT, true },
|
|
mhselect_interpretation{ 6, MHSELECT_MODE_VMID, true },
|
|
std::nullopt
|
|
};
|
|
assert(mhselect < 8);
|
|
unsigned legal = h_enabled ? warlize_if_h[mhselect] : warlize_no_h[mhselect];
|
|
assert(legal < 8);
|
|
return table[legal].value();
|
|
}
|
|
|
|
sselect_t sselect;
|
|
unsigned svalue;
|
|
unsigned sbytemask;
|
|
unsigned mhselect;
|
|
unsigned mhvalue;
|
|
};
|
|
|
|
class disabled_trigger_t : public trigger_t {
|
|
public:
|
|
virtual reg_t tdata1_read(const processor_t * const proc) const noexcept override;
|
|
virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override;
|
|
|
|
virtual bool get_dmode() const override { return dmode; }
|
|
|
|
private:
|
|
bool dmode;
|
|
};
|
|
|
|
class trap_common_t : public trigger_t {
|
|
public:
|
|
bool get_dmode() const override { return dmode; }
|
|
virtual action_t get_action() const override { return action; }
|
|
|
|
virtual std::optional<match_result_t> detect_trap_match(processor_t * const proc, const trap_t& t) noexcept override;
|
|
|
|
private:
|
|
virtual bool simple_match(bool interrupt, reg_t bit) const = 0;
|
|
|
|
protected:
|
|
bool dmode;
|
|
bool hit;
|
|
action_t action;
|
|
};
|
|
|
|
class itrigger_t : public trap_common_t {
|
|
public:
|
|
virtual reg_t tdata1_read(const processor_t * const proc) const noexcept override;
|
|
virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override;
|
|
|
|
private:
|
|
virtual bool simple_match(bool interrupt, reg_t bit) const override;
|
|
bool nmi;
|
|
};
|
|
|
|
class etrigger_t : public trap_common_t {
|
|
public:
|
|
virtual reg_t tdata1_read(const processor_t * const proc) const noexcept override;
|
|
virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override;
|
|
|
|
private:
|
|
virtual bool simple_match(bool interrupt, reg_t bit) const override;
|
|
};
|
|
|
|
class mcontrol_common_t : public trigger_t {
|
|
public:
|
|
typedef enum
|
|
{
|
|
MATCH_EQUAL = MCONTROL_MATCH_EQUAL,
|
|
MATCH_NAPOT = MCONTROL_MATCH_NAPOT,
|
|
MATCH_GE = MCONTROL_MATCH_GE,
|
|
MATCH_LT = MCONTROL_MATCH_LT,
|
|
MATCH_MASK_LOW = MCONTROL_MATCH_MASK_LOW,
|
|
MATCH_MASK_HIGH = MCONTROL_MATCH_MASK_HIGH
|
|
} match_t;
|
|
|
|
virtual bool get_dmode() const override { return dmode; }
|
|
virtual bool get_chain() const override { return chain; }
|
|
virtual bool get_execute() const override { return execute; }
|
|
virtual bool get_store() const override { return store; }
|
|
virtual bool get_load() const override { return load; }
|
|
virtual action_t get_action() const override { return action; }
|
|
virtual void set_hit(hit_t val) = 0;
|
|
|
|
virtual std::optional<match_result_t> detect_memory_access_match(processor_t * const proc,
|
|
operation_t operation, reg_t address, std::optional<reg_t> data) noexcept override;
|
|
|
|
private:
|
|
bool simple_match(unsigned xlen, reg_t value) const;
|
|
|
|
protected:
|
|
static match_t legalize_match(reg_t val, reg_t maskmax) noexcept;
|
|
static bool legalize_timing(reg_t val, reg_t timing_mask, reg_t select_mask, reg_t execute_mask, reg_t load_mask) noexcept;
|
|
bool dmode = false;
|
|
action_t action = ACTION_DEBUG_EXCEPTION;
|
|
bool select = false;
|
|
bool timing = false;
|
|
bool chain = false;
|
|
match_t match = MATCH_EQUAL;
|
|
bool execute = false;
|
|
bool store = false;
|
|
bool load = false;
|
|
};
|
|
|
|
class mcontrol_t : public mcontrol_common_t {
|
|
public:
|
|
virtual reg_t tdata1_read(const processor_t * const proc) const noexcept override;
|
|
virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override;
|
|
|
|
virtual void set_hit(hit_t val) override { hit = val != HIT_FALSE; }
|
|
|
|
private:
|
|
bool hit = false;
|
|
const reg_t maskmax = 0;
|
|
};
|
|
|
|
class mcontrol6_t : public mcontrol_common_t {
|
|
public:
|
|
virtual reg_t tdata1_read(const processor_t * const proc) const noexcept override;
|
|
virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override;
|
|
|
|
virtual void set_hit(hit_t val) override { hit = val; }
|
|
|
|
private:
|
|
hit_t hit = HIT_FALSE;
|
|
};
|
|
|
|
class icount_t : public trigger_t {
|
|
public:
|
|
virtual reg_t tdata1_read(const processor_t * const proc) const noexcept override;
|
|
virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override;
|
|
|
|
bool get_dmode() const override { return dmode; }
|
|
virtual action_t get_action() const override { return action; }
|
|
virtual bool icount_check_needed() const override { return count > 0 || pending; }
|
|
virtual void stash_read_values() override;
|
|
|
|
virtual std::optional<match_result_t> detect_icount_fire(processor_t * const proc) noexcept override;
|
|
virtual void detect_icount_decrement(processor_t * const proc) noexcept override;
|
|
|
|
private:
|
|
bool dmode = false;
|
|
bool hit = false;
|
|
unsigned count = 1, count_read_value = 1;
|
|
bool pending = false, pending_read_value = false;
|
|
action_t action = (action_t)0;
|
|
};
|
|
|
|
class module_t {
|
|
public:
|
|
module_t(unsigned count);
|
|
~module_t();
|
|
|
|
reg_t tdata1_read(unsigned index) const noexcept;
|
|
bool tdata1_write(unsigned index, const reg_t val) noexcept;
|
|
reg_t tdata2_read(unsigned index) const noexcept;
|
|
bool tdata2_write(unsigned index, const reg_t val) noexcept;
|
|
reg_t tdata3_read(unsigned index) const noexcept;
|
|
bool tdata3_write(unsigned index, const reg_t val) noexcept;
|
|
reg_t tinfo_read(unsigned index) const noexcept;
|
|
|
|
unsigned count() const { return triggers.size(); }
|
|
|
|
std::optional<match_result_t> detect_memory_access_match(operation_t operation, reg_t address, std::optional<reg_t> data) noexcept;
|
|
std::optional<match_result_t> detect_icount_match() noexcept;
|
|
std::optional<match_result_t> detect_trap_match(const trap_t& t) noexcept;
|
|
|
|
processor_t *proc;
|
|
private:
|
|
std::vector<trigger_t *> triggers;
|
|
};
|
|
|
|
}
|
|
|
|
#endif
|