diff --git a/riscv/execute.cc b/riscv/execute.cc index f4c88cafa9..e972aa38a5 100644 --- a/riscv/execute.cc +++ b/riscv/execute.cc @@ -2,9 +2,12 @@ #include "config.h" #include "processor.h" +#include "trace_ingress.h" +#include "trace_encoder_n.h" #include "mmu.h" #include "disasm.h" #include "decode_macros.h" +#include "trace_ingress.h" #include static void commit_log_reset(processor_t* p) @@ -162,6 +165,7 @@ inline void processor_t::update_histogram(reg_t pc) static inline reg_t execute_insn_fast(processor_t* p, reg_t pc, insn_fetch_t fetch) { return fetch.func(p, fetch.insn, pc); } + static inline reg_t execute_insn_logged(processor_t* p, reg_t pc, insn_fetch_t fetch) { if (p->get_log_commits_enabled()) { @@ -173,6 +177,21 @@ static inline reg_t execute_insn_logged(processor_t* p, reg_t pc, insn_fetch_t f try { npc = fetch.func(p, fetch.insn, pc); + + if (p->get_trace_enabled()) { + hart_to_encoder_ingress_t packet { + .i_type = _get_insn_type(&fetch.insn, npc != p->get_state()->pc + insn_length(fetch.insn.bits())), + .exc_cause = 0, + .tval = 0, + .priv = P_M, // TODO: check for processor privilege level + .i_addr = pc, + .iretire = 1, + .ilastsize = insn_length(fetch.insn.bits())/2, + .i_timestamp = p->get_state()->mcycle->read(), + }; + p->trace_encoder.push_commit(packet); + } + if (npc != PC_SERIALIZE_BEFORE) { if (p->get_log_commits_enabled()) { commit_log_print_insn(p, pc, fetch.insn); @@ -205,7 +224,7 @@ static inline reg_t execute_insn_logged(processor_t* p, reg_t pc, insn_fetch_t f bool processor_t::slow_path() { return debug || state.single_step != state.STEP_NONE || state.debug_mode || - log_commits_enabled || histogram_enabled || in_wfi || check_triggers_icount; + log_commits_enabled || histogram_enabled || in_wfi || check_triggers_icount || trace_enabled; } // fetch/decode/execute loop diff --git a/riscv/processor.cc b/riscv/processor.cc index 0b318f5e12..0ec8fa773b 100644 --- a/riscv/processor.cc +++ b/riscv/processor.cc @@ -12,6 +12,7 @@ #include "platform.h" #include "vector_unit.h" #include "debug_defines.h" +#include "trace_encoder_n.h" #include #include #include @@ -182,6 +183,11 @@ void processor_t::enable_log_commits() log_commits_enabled = true; } +void processor_t::enable_trace() +{ + trace_enabled = true; +} + void processor_t::reset() { xlen = isa.get_max_xlen(); @@ -204,6 +210,8 @@ void processor_t::reset() if (sim) sim->proc_reset(id); + + trace_encoder.reset(); } extension_t* processor_t::get_extension() diff --git a/riscv/processor.h b/riscv/processor.h index 7744e861ab..08c6838756 100644 --- a/riscv/processor.h +++ b/riscv/processor.h @@ -17,6 +17,7 @@ #include "triggers.h" #include "../fesvr/memif.h" #include "vector_unit.h" +#include "trace_encoder_n.h" #define FIRST_HPMCOUNTER 3 #define N_HPMCOUNTERS 29 @@ -253,7 +254,9 @@ class processor_t : public abstract_device_t void set_debug(bool value); void set_histogram(bool value); void enable_log_commits(); + void enable_trace(); bool get_log_commits_enabled() const { return log_commits_enabled; } + bool get_trace_enabled() const { return trace_enabled; } void reset(); void step(size_t n); // run for n cycles void put_csr(int which, reg_t val); @@ -367,6 +370,8 @@ class processor_t : public abstract_device_t void check_if_lpad_required(); + trace_encoder_n* get_trace_encoder() { return &trace_encoder; } + private: const isa_parser_t isa; const cfg_t * const cfg; @@ -380,6 +385,7 @@ class processor_t : public abstract_device_t unsigned xlen; bool histogram_enabled; bool log_commits_enabled; + bool trace_enabled; // whether core needs to trace instructions - does not mean the encoder itself is enabled! FILE *log_file; std::ostream sout_; // needed for socket command interface -s, also used for -d and -l, but not for --log bool halt_on_reset; @@ -432,6 +438,7 @@ class processor_t : public abstract_device_t vectorUnit_t VU; triggers::module_t TM; + trace_encoder_n trace_encoder; }; #endif diff --git a/riscv/riscv.mk.in b/riscv/riscv.mk.in index bc512bfa41..0b63bb5169 100644 --- a/riscv/riscv.mk.in +++ b/riscv/riscv.mk.in @@ -44,6 +44,8 @@ riscv_install_hdrs = \ trap.h \ triggers.h \ vector_unit.h \ + trace_ingress.h \ + trace_encoder_n.h \ riscv_precompiled_hdrs = \ insn_template.h \ @@ -73,6 +75,7 @@ riscv_srcs = \ vector_unit.cc \ socketif.cc \ cfg.cc \ + trace_encoder_n.cc \ $(riscv_gen_srcs) \ riscv_test_srcs = \ diff --git a/riscv/sim.cc b/riscv/sim.cc index a44aea9413..51fd684d89 100644 --- a/riscv/sim.cc +++ b/riscv/sim.cc @@ -316,15 +316,20 @@ void sim_t::set_histogram(bool value) } } -void sim_t::configure_log(bool enable_log, bool enable_commitlog) +void sim_t::configure_log(bool enable_log, bool enable_commitlog, bool trace) { log = enable_log; - if (!enable_commitlog) - return; + if (enable_commitlog) { + for (processor_t *proc : procs) { + proc->enable_log_commits(); + } + } - for (processor_t *proc : procs) { - proc->enable_log_commits(); + if (trace) { + for (processor_t *proc : procs) { + proc->enable_trace(); + } } } diff --git a/riscv/sim.h b/riscv/sim.h index 93a2c21dde..efeab9a365 100644 --- a/riscv/sim.h +++ b/riscv/sim.h @@ -48,7 +48,7 @@ class sim_t : public htif_t, public simif_t // // If enable_log is true, an instruction trace will be generated. If // enable_commitlog is true, so will the commit results - void configure_log(bool enable_log, bool enable_commitlog); + void configure_log(bool enable_log, bool enable_commitlog, bool trace); void set_procs_debug(bool value); void set_remote_bitbang(remote_bitbang_t* remote_bitbang) { diff --git a/riscv/trace_encoder_n.cc b/riscv/trace_encoder_n.cc new file mode 100644 index 0000000000..24f919ff63 --- /dev/null +++ b/riscv/trace_encoder_n.cc @@ -0,0 +1,288 @@ +#include "trace_encoder_n.h" + +void trace_encoder_n::reset() { + this->state = TRACE_ENCODER_N_IDLE; + this->icnt = 0; + this->packet_0 = hart_to_encoder_ingress_t(); // create empty packet + this->packet_1 = hart_to_encoder_ingress_t(); // create empty packet + this->enabled = false; + this->active = true; +} + +void trace_encoder_n::set_enable(bool enabled) { + bool was_enabled = this->enabled; + this->enabled = enabled; + if (!was_enabled && enabled) { + this->state = TRACE_ENCODER_N_IDLE; + } +} + +void trace_encoder_n::push_commit(hart_to_encoder_ingress_t packet) { + this->packet_1 = this->packet_0; + this->packet_0 = packet; + if (this->enabled) { + fprintf(this->debug_reference, "%lx\n", packet.i_addr); + if (this->state == TRACE_ENCODER_N_IDLE) { + generate_packet(TCODE_PROG_TRACE_SYNC); + this->state = TRACE_ENCODER_N_DATA; + this->icnt += this->packet_0.ilastsize; + } else if (this->state == TRACE_ENCODER_N_DATA) { + this->icnt += this->packet_0.ilastsize; + if (this->packet_1.i_type == I_BRANCH_TAKEN) { + generate_packet(TCODE_DBR); + this->icnt = this->packet_0.ilastsize; + } else if (this->packet_1.i_type == I_JUMP_UNINFERABLE) { + generate_packet(TCODE_IBR); + this->icnt = this->packet_0.ilastsize; + } + this->state = this->icnt >= MAX_ICNT ? TRACE_ENCODER_N_FULL : TRACE_ENCODER_N_DATA; + } else if (this->state == TRACE_ENCODER_N_FULL) { + generate_packet(TCODE_FULL); + this->state = TRACE_ENCODER_N_DATA; + this->icnt = 0; + } + } else if (!this->enabled) { + if (this->state == TRACE_ENCODER_N_DATA) { + generate_packet(TCODE_PROG_TRACE_CORR); + this->state = TRACE_ENCODER_N_IDLE; + this->icnt = 0; + } + } +} + +void print_packet(trace_encoder_n_packet_t* packet) { + printf("[trace_encoder_n] printing packet: tcode: %lx, src: %lx, icnt: %lx, f_addr: %lx, u_addr: %lx, b_type: %lx\n", packet->tcode, packet->src, packet->icnt, packet->f_addr, packet->u_addr, packet->b_type); +} + +void print_encoded_packet(uint8_t* buffer, int num_bytes) { + printf("[trace_encoder_n] encoded packet: "); + for (int i = 0; i < num_bytes; i++) { + printf("%lx ", buffer[i]); + } + printf("\n"); +} + +void trace_encoder_n::generate_packet(tcode_t tcode) { + trace_encoder_n_packet_t packet; + int num_bytes; + this->packet_count++; + switch (tcode) { + case TCODE_PROG_TRACE_SYNC: + set_program_trace_sync_packet(&packet); + print_packet(&packet); + num_bytes = packet_to_buffer(&packet); + fwrite(this->buffer, 1, num_bytes, this->trace_sink); + print_encoded_packet(this->buffer, num_bytes); + break; + case TCODE_DBR: + set_direct_branch_packet(&packet); + print_packet(&packet); + num_bytes = packet_to_buffer(&packet); + fwrite(this->buffer, 1, num_bytes, this->trace_sink); + print_encoded_packet(this->buffer, num_bytes); + break; + case TCODE_IBR: + set_indirect_branch_packet(&packet); + print_packet(&packet); + num_bytes = packet_to_buffer(&packet); + fwrite(this->buffer, 1, num_bytes, this->trace_sink); + print_encoded_packet(this->buffer, num_bytes); + break; + case TCODE_PROG_TRACE_CORR: + set_program_trace_corr_packet(&packet); + print_packet(&packet); + num_bytes = packet_to_buffer(&packet); + fwrite(this->buffer, 1, num_bytes, this->trace_sink); + print_encoded_packet(this->buffer, num_bytes); + break; + default: + break; + } +} + +void trace_encoder_n::set_program_trace_sync_packet(trace_encoder_n_packet_t* packet){ + packet->tcode = TCODE_PROG_TRACE_SYNC; + packet->src = this->src; + packet->sync = SYNC_TRACE_EN; + packet->icnt = 0; + packet->f_addr = this->packet_0.i_addr >> 1; + this->prev_addr = packet->f_addr; + packet->tstamp = this->packet_0.i_timestamp; + this->prev_timestamp = this->packet_0.i_timestamp; +} + +void trace_encoder_n::set_direct_branch_packet(trace_encoder_n_packet_t* packet){ + packet->tcode = TCODE_DBR; + packet->src = this->src; + packet->icnt = this->icnt - this->packet_0.ilastsize; + packet->tstamp = this->packet_1.i_timestamp ^ this->prev_timestamp; + this->prev_timestamp = this->packet_1.i_timestamp; +} + +void trace_encoder_n::set_indirect_branch_packet(trace_encoder_n_packet_t* packet){ + packet->tcode = TCODE_IBR; + packet->src = this->src; + packet->b_type = B_INDIRECT; + packet->icnt = this->icnt - this->packet_0.ilastsize; + uint64_t e_addr = this->packet_0.i_addr >> 1; + packet->u_addr = e_addr ^ this->prev_addr; + this->prev_addr = e_addr; + packet->tstamp = this->packet_1.i_timestamp ^ this->prev_timestamp; + this->prev_timestamp = this->packet_1.i_timestamp; +} + +void trace_encoder_n::set_program_trace_corr_packet(trace_encoder_n_packet_t* packet) { + packet->tcode = TCODE_PROG_TRACE_CORR; + packet->src = this->src; + packet->icnt = this->icnt; + packet->evcode = EVCODE_DISA; + packet->tstamp = this->packet_1.i_timestamp ^ this->prev_timestamp; + this->prev_timestamp = this->packet_1.i_timestamp; +} + +// returns the number of bytes written to the buffer +int trace_encoder_n::packet_to_buffer(trace_encoder_n_packet_t* packet){ + switch (packet->tcode) { + case TCODE_PROG_TRACE_SYNC: + return packet_to_buffer_program_trace_sync_packet(packet); + case TCODE_DBR: + return packet_to_buffer_direct_branch_packet(packet); + case TCODE_IBR: + return packet_to_buffer_indirect_branch_packet(packet); + case TCODE_PROG_TRACE_CORR: + return packet_to_buffer_program_trace_corr_packet(packet); + default: + break; + } +} + +int trace_encoder_n::packet_to_buffer_program_trace_sync_packet(trace_encoder_n_packet_t* packet) { + int msb = find_msb(packet->f_addr); + this->buffer[0] = packet->tcode << MDO_OFFSET | MSEO_IDLE; + // handling sync + this->buffer[1] = packet->sync << MDO_OFFSET | MSEO_IDLE; + // handling f_addr + this->buffer[1] |= (packet->f_addr & 0b11) << 6; + int num_bytes = 0; + if (msb < 2) { + this->buffer[1] |= MSEO_EOF; + } else { + packet->f_addr >>= 2; + msb -= 2; + num_bytes = ceil_div(msb, 6); + for (int iter = 0; iter < num_bytes; iter++) { + this->buffer[2 + iter] = ((packet->f_addr & 0x3F) << MDO_OFFSET) | MSEO_IDLE; + packet->f_addr >>= 6; + } + this->buffer[2 + num_bytes - 1] |= MSEO_EOF; + } + // handling tstamp + int tmsb = find_msb(packet->tstamp); + int tnum_bytes = ceil_div(tmsb, 6); + for (int iter = 0; iter < tnum_bytes; iter++) { + this->buffer[2 + num_bytes + iter] = ((packet->tstamp & 0x3F) << MDO_OFFSET) | MSEO_IDLE; + packet->tstamp >>= 6; + } + this->buffer[2 + num_bytes + tnum_bytes - 1] |= MSEO_LAST; + return 2 + num_bytes + tnum_bytes; +} + +int trace_encoder_n::packet_to_buffer_direct_branch_packet(trace_encoder_n_packet_t* packet) { + this->buffer[0] = packet->tcode << MDO_OFFSET | MSEO_IDLE; + int msb = find_msb(packet->icnt); + int num_bytes = ceil_div(msb, 6); + // handling icnt + for (int iter = 0; iter < num_bytes; iter++) { + this->buffer[1 + iter] = ((packet->icnt & 0x3F) << MDO_OFFSET) | MSEO_IDLE; + packet->icnt >>= 6; + } + this->buffer[1 + num_bytes - 1] |= MSEO_EOF; + // handling tstamp + int tmsb = find_msb(packet->tstamp); + int tnum_bytes = ceil_div(tmsb, 6); + for (int iter = 0; iter < tnum_bytes; iter++) { + this->buffer[1 + num_bytes + iter] = ((packet->tstamp & 0x3F) << MDO_OFFSET) | MSEO_IDLE; + packet->tstamp >>= 6; + } + this->buffer[1 + num_bytes + tnum_bytes - 1] |= MSEO_LAST; + return 1 + num_bytes + tnum_bytes; +} + +int trace_encoder_n::packet_to_buffer_indirect_branch_packet(trace_encoder_n_packet_t* packet) { + this->buffer[0] = packet->tcode << MDO_OFFSET | MSEO_IDLE; + this->buffer[1] = packet->b_type << MDO_OFFSET | MSEO_IDLE; + // icnt + int icnt_msb = find_msb(packet->icnt); + int icnt_num_bytes = 0; + this->buffer[1] |= (packet->icnt & 0xF) << 4; + if (icnt_msb < 4) { + this->buffer[1] |= MSEO_EOF; + } else { + packet->icnt >>= 4; + icnt_msb -= 4; + icnt_num_bytes = ceil_div(icnt_msb, 6); + for (int iter = 0; iter < icnt_num_bytes; iter++) { + this->buffer[2 + iter] = ((packet->icnt & 0x3F) << MDO_OFFSET) | MSEO_IDLE; + packet->icnt >>= 6; + } + this->buffer[2 + icnt_num_bytes - 1] |= MSEO_EOF; + } + // u_addr + int u_addr_start = 2 + icnt_num_bytes; + int u_addr_msb = find_msb(packet->u_addr); + int u_addr_num_bytes = ceil_div(u_addr_msb, 6); + for (int iter = 0; iter < u_addr_num_bytes; iter++) { + this->buffer[u_addr_start + iter] = ((packet->u_addr & 0x3F) << MDO_OFFSET) | MSEO_IDLE; + packet->u_addr >>= 6; + } + this->buffer[u_addr_start + u_addr_num_bytes - 1] |= MSEO_EOF; + // handling tstamp + int tmsb = find_msb(packet->tstamp); + int tnum_bytes = ceil_div(tmsb, 6); + for (int iter = 0; iter < tnum_bytes; iter++) { + this->buffer[u_addr_start + u_addr_num_bytes + iter] = ((packet->tstamp & 0x3F) << MDO_OFFSET) | MSEO_IDLE; + packet->tstamp >>= 6; + } + this->buffer[u_addr_start + u_addr_num_bytes + tnum_bytes - 1] |= MSEO_LAST; + return u_addr_start + u_addr_num_bytes + tnum_bytes; +} + +int trace_encoder_n::packet_to_buffer_program_trace_corr_packet(trace_encoder_n_packet_t* packet) { + int msb = find_msb(packet->icnt); + this->buffer[0] = packet->tcode << MDO_OFFSET | MSEO_IDLE; + this->buffer[1] = packet->evcode << MDO_OFFSET | MSEO_IDLE; + this->buffer[1] |= (packet->icnt & 0b11) << 6; + int num_bytes = 0; + if (msb < 2) { + this->buffer[1] |= MSEO_EOF; + } else { + packet->icnt >>= 2; + msb -= 2; + num_bytes = ceil_div(msb, 6); + for (int iter = 0; iter < num_bytes; iter++) { + this->buffer[2 + iter] = ((packet->icnt & 0x3F) << MDO_OFFSET) | MSEO_IDLE; + packet->icnt >>= 6; + } + this->buffer[2 + num_bytes - 1] |= MSEO_EOF; + } + // handling tstamp + int tmsb = find_msb(packet->tstamp); + int tnum_bytes = ceil_div(tmsb, 6); + for (int iter = 0; iter < tnum_bytes; iter++) { + this->buffer[2 + num_bytes + iter] = ((packet->tstamp & 0x3F) << MDO_OFFSET) | MSEO_IDLE; + packet->tstamp >>= 6; + } + this->buffer[2 + num_bytes + tnum_bytes - 1] |= MSEO_LAST; + return 2 + num_bytes + tnum_bytes; +} + +// returns the 0-index of the most significant bit +int find_msb(uint64_t x) { + if (x == 0) return -1; + return 63 - __builtin_clzll(x); +} + +// returns the ceiling of the division of a 0-indexed a by b +int ceil_div(int a, int b) { + return a / b + 1; +} \ No newline at end of file diff --git a/riscv/trace_encoder_n.h b/riscv/trace_encoder_n.h new file mode 100644 index 0000000000..25e49af536 --- /dev/null +++ b/riscv/trace_encoder_n.h @@ -0,0 +1,126 @@ +#ifndef _RISCV_TRACE_ENCODER_N_H +#define _RISCV_TRACE_ENCODER_N_H + +#include "trace_ingress.h" +#include + +class processor_t; + +enum tcode_t { + TCODE_OWN = 2, // ownership + TCODE_DBR = 3, // direct branch + TCODE_IBR = 4, // indirect branch + TCODE_ERR = 8, // error + TCODE_PROG_TRACE_SYNC = 9, // program trace sync + TCODE_DBR_SYNC = 10, // direct branch sync + TCODE_IBR_SYNC = 11, // indirect branch sync + TCODE_FULL = 27, // resource full + TCODE_IBR_HIST = 28, // indirect branch history + TCODE_IBR_HIST_SYNC = 29, // indirect branch history sync + TCODE_RBR = 30, // repeated branches + TCODE_PROG_TRACE_CORR = 33 // program trace correlation +}; + +enum b_type_t { + B_INDIRECT = 0, // indirect branch + B_TRAP = 1, // trap + B_EXCEPTION = 2, // exception + B_INTERRUPT = 3 // interrupt +}; + +enum sync_t { + SYNC_TRACE_EN = 5, +}; + +enum evcode_t { + EVCODE_DISA = 4, +}; + +struct trace_encoder_n_packet_t { + uint8_t size; // 8 bits + tcode_t tcode; // 6 bits + uint16_t src; // unused for now + uint8_t sync; // 4 bit + uint8_t b_type; // 2 bits + uint8_t evcode; // 4 bits + uint16_t icnt; // 16 bits + uint64_t f_addr; // 64 bits + uint64_t u_addr; // 64 bits + uint64_t tstamp; +}; + +void print_packet(trace_encoder_n_packet_t* packet); +void print_encoded_packet(uint8_t* buffer, int num_bytes); + +int find_msb(uint64_t x); +int ceil_div(int a, int b); + +enum trace_encoder_n_state_t { + TRACE_ENCODER_N_IDLE, + TRACE_ENCODER_N_DATA, + TRACE_ENCODER_N_FULL +}; + +#define MAX_TRACE_BUFFER_SIZE 38 + +#define MDO_OFFSET 2 + +#define MSEO_IDLE 0b00 +#define MSEO_EOF 0b01 +#define MSEO_RES 0b10 // reserved +#define MSEO_LAST 0b11 + +#define MAX_ICNT_BITS 15 +#define MAX_ICNT 1 << MAX_ICNT_BITS + +class trace_encoder_n { +public: + + trace_encoder_n() { + this->trace_sink= fopen("trace_n.bin", "wb"); + this->debug_reference = fopen("trace_n_ref_debug.log", "wb"); + this->active = true; + this->enabled = false; + this->src = 0; + this->state = TRACE_ENCODER_N_IDLE; + this->icnt = 0; + this->packet_0 = hart_to_encoder_ingress_t(); // create empty packet + this->packet_1 = hart_to_encoder_ingress_t(); // create empty packet + this->packet_count = 0; + } + void push_commit(hart_to_encoder_ingress_t packet); + void generate_packet(tcode_t tcode); + + void set_enable(bool enabled); + void reset(); +private: + FILE* trace_sink; + FILE* debug_reference; + + hart_to_encoder_ingress_t packet_0; // the newer packet + hart_to_encoder_ingress_t packet_1; // the older packet + uint64_t packet_count; + + bool active; + bool enabled; + uint16_t src; + trace_encoder_n_state_t state; + uint8_t buffer[MAX_TRACE_BUFFER_SIZE]; + uint16_t icnt; + uint64_t prev_addr; + uint64_t prev_timestamp; + + void set_direct_branch_packet(trace_encoder_n_packet_t* packet); + void set_indirect_branch_packet(trace_encoder_n_packet_t* packet); + void set_program_trace_sync_packet(trace_encoder_n_packet_t* packet); + void set_program_trace_corr_packet(trace_encoder_n_packet_t* packet); + + int packet_to_buffer(trace_encoder_n_packet_t* packet); + int packet_to_buffer_program_trace_sync_packet(trace_encoder_n_packet_t* packet); + int packet_to_buffer_direct_branch_packet(trace_encoder_n_packet_t* packet); + int packet_to_buffer_indirect_branch_packet(trace_encoder_n_packet_t* packet); + int packet_to_buffer_program_trace_corr_packet(trace_encoder_n_packet_t* packet); +}; + + +#endif \ No newline at end of file diff --git a/riscv/trace_ingress.h b/riscv/trace_ingress.h new file mode 100644 index 0000000000..fbd8790e2f --- /dev/null +++ b/riscv/trace_ingress.h @@ -0,0 +1,99 @@ +#ifndef _RISCV_TRACE_INGRESS_H +#define _RISCV_TRACE_INGRESS_H + +#include +#include "encoding.h" +#include "decode.h" + +enum trace_encoder_type { + N_TRACE_ENCODER, + E_TRACE_ENCODER +}; + +// Enumerates all possible instruction types +enum insn_type { + I_NONE = 0, + I_EXCEPTION = 1, + I_INTERRUPT = 2, + I_TRAP_RETURN = 3, + I_BRANCH_NON_TAKEN = 4, + I_BRANCH_TAKEN = 5, + // I_BRANCH_UNINFERABLE = 6, + I_RESERVED = 7, + I_CALL_UNINFERABLE = 8, + I_CALL_INFERABLE = 9, + I_JUMP_UNINFERABLE = 10, + I_JUMP_INFERABLE = 11, + I_COROUTINE_SWAP = 12, + I_RETURN = 13, + I_OTHER_UNINFERABLE = 14, + I_OTHER_INFERABLE = 15, +}; + +// Enumerates all possible privilege level encodings +enum priv_enc { + P_U = 0, + P_S = 1, + // 2 is reserved + P_M = 3, + // 4-7 is optional and unused for now + P_D = 4, + P_VU = 5, + P_VS = 6, + // 7 is reserved +}; + +// Core to encoder communication struct +// Assume a spike hart with max 1 instruction retired per cycle +// Regardless of RV64 or RV32, we fix the size of the instruction address to 64 bits +struct hart_to_encoder_ingress_t { + // instruction type + insn_type i_type; // 4 bits + // exception cause, mcause + uint8_t exc_cause; // 4 bits (only the lower 4 bits are used) + // trap value, mtval + uint64_t tval; // 64 bits + // core privilege level + priv_enc priv; // 3 bits + // retired instruction address + uint64_t i_addr; // 64 bits + //context, time, ctype, sijump are now unimplemented + // number of instructions retired - 0 or 1 for spike harts + bool iretire; // 1 bit + // ilastsize + int ilastsize; // 2 or 4, 2 bits + // timestamp + uint64_t i_timestamp; // 64 bits +}; + +#define CHECK_INSN(name) ((insn->bits() & MASK_##name) == MATCH_##name) + +static inline bool _is_branch(insn_t* insn) { + return CHECK_INSN(BEQ) || CHECK_INSN(BNE) || CHECK_INSN(BLT) || CHECK_INSN(BGE) || CHECK_INSN(BLTU) || CHECK_INSN(BGEU) || CHECK_INSN(C_BEQZ) || CHECK_INSN(C_BNEZ); +} + +static inline bool _is_jal(insn_t* insn) { + return CHECK_INSN(JAL) || CHECK_INSN(C_JAL) || CHECK_INSN(C_J); +} + +static inline bool _is_jalr(insn_t* insn) { + return CHECK_INSN(JALR) || CHECK_INSN(C_JALR) || CHECK_INSN(C_JR); +} + +static inline insn_type _get_insn_type(insn_t* insn, bool taken) { + if (_is_branch(insn)) { + return taken ? I_BRANCH_TAKEN : I_BRANCH_NON_TAKEN; + } + else if (_is_jal(insn)) { + return I_JUMP_INFERABLE; + } + else if (_is_jalr(insn)) { + return I_JUMP_UNINFERABLE; + } + // TODO: further categorization is not implemented for now + else { + return I_NONE; + } +} + +#endif // _RISCV_TRACE_INGRESS_H \ No newline at end of file diff --git a/spike_main/spike.cc b/spike_main/spike.cc index 1a298f2d5c..685950f345 100644 --- a/spike_main/spike.cc +++ b/spike_main/spike.cc @@ -56,6 +56,7 @@ static void help(int exit_code = 1) fprintf(stderr, " specify --device=, to pass down extra args.\n"); fprintf(stderr, " --log-cache-miss Generate a log of cache miss\n"); fprintf(stderr, " --log-commits Generate a log of commits info\n"); + fprintf(stderr, " --trace Generate a trace of instructions\n"); fprintf(stderr, " --extension= Specify RoCC Extension\n"); fprintf(stderr, " This flag can be used multiple times.\n"); fprintf(stderr, " --extlib= Shared library to load\n"); @@ -340,6 +341,7 @@ int main(int argc, char** argv) std::unique_ptr l2; bool log_cache = false; bool log_commits = false; + bool trace = false; const char *log_path = nullptr; std::vector> extensions; const char* initrd = NULL; @@ -442,6 +444,8 @@ int main(int argc, char** argv) [&](const char UNUSED *s){dm_config.support_haltgroups = false;}); parser.option(0, "log-commits", 0, [&](const char UNUSED *s){log_commits = true;}); + parser.option(0, "trace", 0, + [&](const char UNUSED *s){trace = true;}); parser.option(0, "log", 1, [&](const char* s){log_path = s;}); FILE *cmd_file = NULL; @@ -550,7 +554,7 @@ int main(int argc, char** argv) } s.set_debug(debug); - s.configure_log(log, log_commits); + s.configure_log(log, log_commits, trace); s.set_histogram(histogram); auto return_code = s.run();