Browse Source

Merge pull request #1160 from YenHaoChen/pr-textra

Implement textra (tdata3) of triggers
pull/1171/head
Andrew Waterman 3 years ago
committed by GitHub
parent
commit
921d56ca7a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      riscv/csrs.cc
  2. 8
      riscv/csrs.h
  3. 7
      riscv/processor.cc
  4. 2
      riscv/processor.h
  5. 105
      riscv/triggers.cc
  6. 65
      riscv/triggers.h

22
riscv/csrs.cc

@ -1134,11 +1134,11 @@ tdata1_csr_t::tdata1_csr_t(processor_t* const proc, const reg_t addr):
}
reg_t tdata1_csr_t::read() const noexcept {
return proc->TM.tdata1_read(proc, state->tselect->read());
return proc->TM.tdata1_read(state->tselect->read());
}
bool tdata1_csr_t::unlogged_write(const reg_t val) noexcept {
return proc->TM.tdata1_write(proc, state->tselect->read(), val);
return proc->TM.tdata1_write(state->tselect->read(), val);
}
tdata2_csr_t::tdata2_csr_t(processor_t* const proc, const reg_t addr):
@ -1146,11 +1146,23 @@ tdata2_csr_t::tdata2_csr_t(processor_t* const proc, const reg_t addr):
}
reg_t tdata2_csr_t::read() const noexcept {
return proc->TM.tdata2_read(proc, state->tselect->read());
return proc->TM.tdata2_read(state->tselect->read());
}
bool tdata2_csr_t::unlogged_write(const reg_t val) noexcept {
return proc->TM.tdata2_write(proc, state->tselect->read(), val);
return proc->TM.tdata2_write(state->tselect->read(), val);
}
tdata3_csr_t::tdata3_csr_t(processor_t* const proc, const reg_t addr):
csr_t(proc, addr) {
}
reg_t tdata3_csr_t::read() const noexcept {
return proc->TM.tdata3_read(state->tselect->read());
}
bool tdata3_csr_t::unlogged_write(const reg_t val) noexcept {
return proc->TM.tdata3_write(state->tselect->read(), val);
}
tinfo_csr_t::tinfo_csr_t(processor_t* const proc, const reg_t addr) :
@ -1158,7 +1170,7 @@ tinfo_csr_t::tinfo_csr_t(processor_t* const proc, const reg_t addr) :
}
reg_t tinfo_csr_t::read() const noexcept {
return proc->TM.tinfo_read(proc, state->tselect->read());
return proc->TM.tinfo_read(state->tselect->read());
}
debug_mode_csr_t::debug_mode_csr_t(processor_t* const proc, const reg_t addr):

8
riscv/csrs.h

@ -612,6 +612,14 @@ class tdata2_csr_t: public csr_t {
virtual bool unlogged_write(const reg_t val) noexcept override;
};
class tdata3_csr_t: public csr_t {
public:
tdata3_csr_t(processor_t* const proc, const reg_t addr);
virtual reg_t read() const noexcept override;
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
};
class tinfo_csr_t: public csr_t {
public:
tinfo_csr_t(processor_t* const proc, const reg_t addr);

7
riscv/processor.cc

@ -390,8 +390,13 @@ void state_t::reset(processor_t* const proc, reg_t max_isa)
csrmap[CSR_TDATA1] = std::make_shared<tdata1_csr_t>(proc, CSR_TDATA1);
csrmap[CSR_TDATA2] = tdata2 = std::make_shared<tdata2_csr_t>(proc, CSR_TDATA2);
csrmap[CSR_TDATA3] = std::make_shared<const_csr_t>(proc, CSR_TDATA3, 0);
csrmap[CSR_TDATA3] = std::make_shared<tdata3_csr_t>(proc, CSR_TDATA3);
csrmap[CSR_TINFO] = std::make_shared<tinfo_csr_t>(proc, CSR_TINFO);
unsigned scontext_length = (xlen == 32 ? 16 : 34); // debug spec suggests 16-bit for RV32 and 34-bit for RV64
csrmap[CSR_SCONTEXT] = scontext = std::make_shared<masked_csr_t>(proc, CSR_SCONTEXT, (reg_t(1) << scontext_length) - 1, 0);
unsigned hcontext_length = (xlen == 32 ? 6 : 13) + (proc->extension_enabled('H') ? 1 : 0); // debug spec suggest 7-bit (6-bit) for RV32 and 14-bit (13-bit) for RV64 with (without) H extension
csrmap[CSR_HCONTEXT] = std::make_shared<masked_csr_t>(proc, CSR_HCONTEXT, (reg_t(1) << hcontext_length) - 1, 0);
csrmap[CSR_MCONTEXT] = mcontext = std::make_shared<proxy_csr_t>(proc, CSR_MCONTEXT, csrmap[CSR_HCONTEXT]);
debug_mode = false;
single_step = STEP_NONE;

2
riscv/processor.h

@ -177,6 +177,8 @@ struct state_t
dcsr_csr_t_p dcsr;
csr_t_p tselect;
tdata2_csr_t_p tdata2;
csr_t_p scontext;
csr_t_p mcontext;
csr_t_p jvt;

105
riscv/triggers.cc

@ -3,6 +3,18 @@
#include "processor.h"
#include "triggers.h"
#define ASIDMAX(SXLEN) (SXLEN == 32 ? 9 : 16)
#define SATP_ASID(SXLEN) (SXLEN == 32 ? SATP32_ASID : SATP64_ASID)
#define VMIDMAX(HSXLEN) (HSXLEN == 32 ? 7 : 14)
#define HGATP_VMID(HSXLEN) (HSXLEN == 32 ? HGATP32_VMID : HGATP64_VMID)
#define CSR_TEXTRA_MHVALUE_LENGTH(XLEN) (XLEN == 32 ? CSR_TEXTRA32_MHVALUE_LENGTH : CSR_TEXTRA64_MHVALUE_LENGTH)
#define CSR_TEXTRA_MHVALUE(XLEN) (XLEN == 32 ? CSR_TEXTRA32_MHVALUE : CSR_TEXTRA64_MHVALUE)
#define CSR_TEXTRA_MHSELECT(XLEN) (XLEN == 32 ? CSR_TEXTRA32_MHSELECT : CSR_TEXTRA64_MHSELECT)
#define CSR_TEXTRA_SBYTEMASK(XLEN) (XLEN == 32 ? CSR_TEXTRA32_SBYTEMASK : CSR_TEXTRA64_SBYTEMASK)
#define CSR_TEXTRA_SVALUE(XLEN) (XLEN == 32 ? CSR_TEXTRA32_SVALUE : CSR_TEXTRA64_SVALUE)
#define CSR_TEXTRA_SSELECT(XLEN) (XLEN == 32 ? CSR_TEXTRA32_SSELECT : CSR_TEXTRA64_SSELECT)
namespace triggers {
reg_t trigger_t::tdata2_read(const processor_t UNUSED * const proc) const noexcept {
@ -17,6 +29,68 @@ action_t trigger_t::legalize_action(reg_t val) const noexcept {
return (val > ACTION_MAXVAL || (val == ACTION_DEBUG_MODE && get_dmode() == 0)) ? ACTION_DEBUG_EXCEPTION : (action_t)val;
}
unsigned trigger_t::legalize_mhselect(bool h_enabled) const noexcept {
const auto interp = interpret_mhselect(h_enabled);
return interp.mhselect;
}
reg_t trigger_t::tdata3_read(const processor_t * const proc) const noexcept {
auto xlen = proc->get_xlen();
reg_t tdata3 = 0;
tdata3 = set_field(tdata3, CSR_TEXTRA_MHVALUE(xlen), mhvalue);
tdata3 = set_field(tdata3, CSR_TEXTRA_MHSELECT(xlen), legalize_mhselect(proc->extension_enabled('H')));
tdata3 = set_field(tdata3, CSR_TEXTRA_SBYTEMASK(xlen), sbytemask);
tdata3 = set_field(tdata3, CSR_TEXTRA_SVALUE(xlen), svalue);
tdata3 = set_field(tdata3, CSR_TEXTRA_SSELECT(xlen), sselect);
return tdata3;
}
void trigger_t::tdata3_write(processor_t * const proc, const reg_t val) noexcept {
auto xlen = proc->get_xlen();
mhvalue = get_field(val, CSR_TEXTRA_MHVALUE(xlen));
mhselect = get_field(val, CSR_TEXTRA_MHSELECT(xlen));
sbytemask = get_field(val, CSR_TEXTRA_SBYTEMASK(xlen));
svalue = proc->extension_enabled_const('S') ? get_field(val, CSR_TEXTRA_SVALUE(xlen)) : 0;
sselect = (sselect_t)((proc->extension_enabled_const('S') && get_field(val, CSR_TEXTRA_SSELECT(xlen)) <= SSELECT_MAXVAL) ? get_field(val, CSR_TEXTRA_SSELECT(xlen)) : SSELECT_IGNORE);
}
bool trigger_t::textra_match(processor_t * const proc) const noexcept
{
auto xlen = proc->get_xlen();
auto hsxlen = proc->get_xlen(); // use xlen since no hsxlen
state_t * const state = proc->get_state();
assert(sselect <= SSELECT_MAXVAL);
if (sselect == SSELECT_SCONTEXT) {
reg_t mask = (reg_t(1) << ((xlen == 32) ? CSR_TEXTRA32_SVALUE_LENGTH : CSR_TEXTRA64_SVALUE_LENGTH)) - 1;
assert(CSR_TEXTRA32_SBYTEMASK_LENGTH < CSR_TEXTRA64_SBYTEMASK_LENGTH);
for (int i = 0; i < CSR_TEXTRA64_SBYTEMASK_LENGTH; i++)
if (sbytemask & (1 << i))
mask &= 0xff << (i * 8);
if ((state->scontext->read() & mask) != (svalue & mask))
return false;
} else if (sselect == SSELECT_ASID) {
const reg_t satp = state->satp->read();
const reg_t asid = get_field(satp, SATP_ASID(xlen));
if (asid != (svalue & ((1 << ASIDMAX(xlen)) - 1)))
return false;
}
const auto mhselect_interp = interpret_mhselect(proc->extension_enabled('H'));
const mhselect_mode_t mode = mhselect_interp.mode;
if (mode == MHSELECT_MODE_MCONTEXT) { // 4, 1, and 5 are mcontext
reg_t mask = (1 << (CSR_TEXTRA_MHVALUE_LENGTH(xlen) + 1)) - 1;
if ((state->mcontext->read() & mask) != mhselect_interp.compare_val(mhvalue))
return false;
} else if (mode == MHSELECT_MODE_VMID) { // 2 and 6 are vmid
const reg_t vmid = get_field(state->hgatp->read(), HGATP_VMID(hsxlen));
if (vmid != (mhselect_interp.compare_val(mhvalue) & ((1 << VMIDMAX(hsxlen)) - 1)))
return false;
}
return true;
}
reg_t disabled_trigger_t::tdata1_read(const processor_t * const proc) const noexcept
{
auto xlen = proc->get_xlen();
@ -270,12 +344,12 @@ module_t::~module_t() {
}
}
reg_t module_t::tdata1_read(const processor_t * const proc, unsigned index) const noexcept
reg_t module_t::tdata1_read(unsigned index) const noexcept
{
return triggers[index]->tdata1_read(proc);
}
bool module_t::tdata1_write(processor_t * const proc, unsigned index, const reg_t val) noexcept
bool module_t::tdata1_write(unsigned index, const reg_t val) noexcept
{
if (triggers[index]->get_dmode() && !proc->get_state()->debug_mode) {
return false;
@ -290,6 +364,7 @@ bool module_t::tdata1_write(processor_t * const proc, unsigned index, const reg_
unsigned type = get_field(val, CSR_TDATA1_TYPE(xlen));
reg_t tdata1 = val;
reg_t tdata2 = triggers[index]->tdata2_read(proc);
reg_t tdata3 = triggers[index]->tdata3_read(proc);
// hardware must zero chain in writes that set dmode to 0 if the next trigger has dmode of 1
const bool allow_chain = !(index+1 < triggers.size() && triggers[index+1]->get_dmode() && !get_field(val, CSR_TDATA1_DMODE(xlen)));
@ -312,16 +387,17 @@ bool module_t::tdata1_write(processor_t * const proc, unsigned index, const reg_
triggers[index]->tdata1_write(proc, tdata1, allow_chain);
triggers[index]->tdata2_write(proc, tdata2);
triggers[index]->tdata3_write(proc, tdata3);
proc->trigger_updated(triggers);
return true;
}
reg_t module_t::tdata2_read(const processor_t * const proc, unsigned index) const noexcept
reg_t module_t::tdata2_read(unsigned index) const noexcept
{
return triggers[index]->tdata2_read(proc);
}
bool module_t::tdata2_write(processor_t * const proc, unsigned index, const reg_t val) noexcept
bool module_t::tdata2_write(unsigned index, const reg_t val) noexcept
{
if (triggers[index]->get_dmode() && !proc->get_state()->debug_mode) {
return false;
@ -331,6 +407,21 @@ bool module_t::tdata2_write(processor_t * const proc, unsigned index, const reg_
return true;
}
reg_t module_t::tdata3_read(unsigned index) const noexcept
{
return triggers[index]->tdata3_read(proc);
}
bool module_t::tdata3_write(unsigned index, const reg_t val) noexcept
{
if (triggers[index]->get_dmode() && !proc->get_state()->debug_mode) {
return false;
}
triggers[index]->tdata3_write(proc, val);
proc->trigger_updated(triggers);
return true;
}
std::optional<match_result_t> module_t::detect_memory_access_match(operation_t operation, reg_t address, std::optional<reg_t> data) noexcept
{
state_t * const state = proc->get_state();
@ -351,7 +442,7 @@ std::optional<match_result_t> module_t::detect_memory_access_match(operation_t o
* entire chain did not match. This is allowed by the spec, because the final
* trigger in the chain will never get `hit` set unless the entire chain
* matches. */
auto result = trigger->detect_memory_access_match(proc, operation, address, data);
auto result = trigger->textra_match(proc) ? trigger->detect_memory_access_match(proc, operation, address, data) : std::nullopt;
if (result.has_value() && !trigger->get_chain())
return result;
@ -367,14 +458,14 @@ std::optional<match_result_t> module_t::detect_trap_match(const trap_t& t) noexc
return std::nullopt;
for (auto trigger: triggers) {
auto result = trigger->detect_trap_match(proc, t);
auto result = trigger->textra_match(proc) ? trigger->detect_trap_match(proc, t) : std::nullopt;
if (result.has_value())
return result;
}
return std::nullopt;
}
reg_t module_t::tinfo_read(UNUSED const processor_t * const proc, unsigned UNUSED index) const noexcept
reg_t module_t::tinfo_read(unsigned UNUSED index) const noexcept
{
/* In spike, every trigger supports the same types. */
return (1 << CSR_TDATA1_TYPE_MCONTROL) |

65
riscv/triggers.h

@ -29,6 +29,19 @@ typedef enum {
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;
@ -57,6 +70,8 @@ public:
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; }
@ -68,10 +83,48 @@ public:
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_trap_match(processor_t UNUSED * const proc, const trap_t UNUSED & t) noexcept { return std::nullopt; }
bool textra_match(processor_t * const proc) const noexcept;
protected:
action_t legalize_action(reg_t val) const noexcept;
reg_t tdata2;
private:
unsigned legalize_mhselect(bool h_enabled) 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 {
@ -176,11 +229,13 @@ public:
module_t(unsigned count);
~module_t();
reg_t tdata1_read(const processor_t * const proc, unsigned index) const noexcept;
bool tdata1_write(processor_t * const proc, unsigned index, const reg_t val) noexcept;
reg_t tdata2_read(const processor_t * const proc, unsigned index) const noexcept;
bool tdata2_write(processor_t * const proc, unsigned index, const reg_t val) noexcept;
reg_t tinfo_read(const processor_t * const proc, unsigned index) const noexcept;
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(); }

Loading…
Cancel
Save