171 lines
6.9 KiB
C++
171 lines
6.9 KiB
C++
#ifndef LIBCPU_EVENT_HH
|
|
#define LIBCPU_EVENT_HH
|
|
|
|
#include <ios>
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
#include <cstdint>
|
|
|
|
/**
|
|
* @file event.hh
|
|
* @brief Definition of CPU event types for debugging and difftest
|
|
*/
|
|
|
|
namespace libcpu {
|
|
|
|
/**
|
|
* @enum event_type_t
|
|
* @brief Enumeration of different CPU event types
|
|
*
|
|
* Each event type has specific meanings for its val1 and val2 fields
|
|
*/
|
|
enum class event_type_t: uint8_t {
|
|
empty = 0, ///< Empty event, used for internal purpose only
|
|
issue, ///< Instruction issued - val1: instr_part1, val2: instr_part2 or zero
|
|
reg_write, ///< Register written - val1: rd_addr, val2: rd_data
|
|
load, ///< Memory load - val1: addr, val2: zero extended data or zero
|
|
store, ///< Memory store - val1: addr, val2: zero extended data
|
|
call, ///< Function call - val1: target_addr, val2: source_stack_pointer
|
|
call_ret, ///< Function return - val1: target_addr, val2: target_stack_pointer
|
|
trap, ///< Trap handling - val1: mcause, val2: mtval
|
|
trap_ret, ///< Trap return - val1: target_addr, val2: custom value
|
|
diff_error, ///< Difftest error - val1: event_type, val2: instr_part1
|
|
n_event_type ///< A place holder indicating the number of event types
|
|
};
|
|
|
|
/**
|
|
* @brief Convert an event_type_t to its string representation
|
|
* @param type The event type to convert
|
|
* @return String representation of the event type
|
|
*/
|
|
inline const char *event_type_to_str(event_type_t type) {
|
|
switch (type) {
|
|
case event_type_t::issue: return "issue";
|
|
case event_type_t::reg_write: return "reg_write";
|
|
case event_type_t::load: return "load";
|
|
case event_type_t::store: return "store";
|
|
case event_type_t::call: return "call";
|
|
case event_type_t::call_ret: return "call_ret";
|
|
case event_type_t::trap: return "trap";
|
|
case event_type_t::trap_ret: return "trap_ret";
|
|
case event_type_t::diff_error: return "diff_error";
|
|
default: return "unknown";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @struct event_t
|
|
* @brief Template structure representing a CPU event
|
|
*
|
|
* `libanemo` does not enforce strict semantics for each field. The implementation treats
|
|
* `val1` and `val2` as generic values whose concrete meanings are context-dependent.
|
|
* This design choice maintains simplicity, flexibility, and performance,
|
|
* However, their interpretation must remain consistent across different implementations
|
|
* during differential testing.
|
|
*
|
|
* For example:
|
|
* - For `load` events:
|
|
* - When representing a load attempt that might trap: `val2` must be zero
|
|
* - When representing a successful load: `val2` must contain the zero-extended data
|
|
* - For `store` events:
|
|
* - May represent either a store attempt or a successful store operation
|
|
* - For `trap_ret` events:
|
|
* - May represent either an attempt of xRET on RISC-V, or a successful xRET
|
|
* - `val2` is for custom uses
|
|
*
|
|
* Certain library components may impose additional requirements. For example,
|
|
* some `io_dispatcher` subclasses requires `load` to represent only successful loads.
|
|
*
|
|
* @tparam WORD_T The word type used for event values (typically uint32_t or uint64_t)
|
|
*/
|
|
template <typename WORD_T>
|
|
struct event_t {
|
|
event_type_t type; ///< Type of the event
|
|
WORD_T pc; ///< Program counter associated with the event
|
|
WORD_T val1; ///< Event-specific primary value (interpretation depends on event type)
|
|
WORD_T val2; ///< Event-specific secondary value (interpretation depends on event type)
|
|
|
|
/**
|
|
* @brief Equality comparison operator for event_t
|
|
* @param other The event to compare with
|
|
* @return true if all fields are equal, false otherwise
|
|
*/
|
|
bool operator==(const event_t& other) const {
|
|
return type == other.type &&
|
|
pc == other.pc &&
|
|
val1 == other.val1 &&
|
|
val2 == other.val2;
|
|
}
|
|
|
|
/**
|
|
* @brief Inequality comparison operator for event_t
|
|
* @param other The event to compare with
|
|
* @return true if any field is not equal, false otherwise
|
|
*/
|
|
bool operator!=(const event_t& other) const {
|
|
return !(*this == other);
|
|
}
|
|
|
|
/**
|
|
* @brief Strict weak ordering comparison for `event_t` objects.
|
|
*
|
|
* This operator defines a lexicographical order for events based on their fields.
|
|
*
|
|
* @note This ordering is required for use with standard library containers
|
|
* (e.g., `std::set`, `std::map`) and algorithms (e.g., `std::sort`).
|
|
*/
|
|
bool operator < (const event_t<WORD_T>& other) const {
|
|
// Compare 'type'
|
|
if (static_cast<WORD_T>(type) != static_cast<WORD_T>(other.type)) {
|
|
return static_cast<WORD_T>(type) < static_cast<WORD_T>(other.type);
|
|
}
|
|
// Compare 'pc'
|
|
if (pc != other.pc) {
|
|
return pc < other.pc;
|
|
}
|
|
// Compare 'val1'
|
|
if (val1 != other.val1) {
|
|
return val1 < other.val1;
|
|
}
|
|
// Compare 'val2'
|
|
return val2 < other.val2;
|
|
}
|
|
|
|
/**
|
|
* @brief Convert the event to a human-readable string
|
|
* @return String representation of the event with appropriate field labels
|
|
*/
|
|
std::string to_string() const {
|
|
const char *label1 = "";
|
|
const char *label2 = "";
|
|
int label_width = 8; // Width for field labels
|
|
|
|
// Determine labels based on event type
|
|
switch (type) {
|
|
case event_type_t::issue: label1 = "instr1"; label2 = "instr2"; break;
|
|
case event_type_t::reg_write: label1 = "rd_addr"; label2 = "rd_data"; break;
|
|
case event_type_t::load: label1 = "addr"; label2 = "data"; break;
|
|
case event_type_t::store: label1 = "addr"; label2 = "data"; break;
|
|
case event_type_t::call: label1 = "target"; label2 = "sp"; break;
|
|
case event_type_t::call_ret: label1 = "target"; label2 = "sp"; break;
|
|
case event_type_t::trap: label1 = "mcause"; label2 = "mtval"; break;
|
|
case event_type_t::trap_ret: label1 = "target"; label2 = "mstatus"; break;
|
|
case event_type_t::diff_error: label1 = "err_type"; label2 = "instr"; break;
|
|
default: label1 = "val1"; label2 = "val2"; break;
|
|
}
|
|
|
|
std::ostringstream oss;
|
|
oss << std::left << std::setw(10) << std::setfill(' ') << event_type_to_str(type)
|
|
<< " pc:0x" << std::hex << std::setw(sizeof(WORD_T)*2) << std::setfill('0') << std::right << pc << " "
|
|
<< std::left << std::setw(label_width) << std::setfill(' ') << std::right << label1 << ":0x" << std::hex << std::setw(sizeof(WORD_T)*2) << std::setfill('0') << val1 << " "
|
|
<< std::left << std::setw(label_width) << std::setfill(' ') << std::right << label2 << ":0x" << std::hex << std::setw(sizeof(WORD_T)*2) << std::setfill('0') << val2;
|
|
|
|
return oss.str();
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
#endif
|