Looks like single step works.
[riscv-isa-sim.git] / riscv / execute.cc
1 // See LICENSE for license details.
2
3 #include "processor.h"
4 #include "mmu.h"
5 #include <cassert>
6
7
8 static void commit_log_stash_privilege(state_t* state)
9 {
10 #ifdef RISCV_ENABLE_COMMITLOG
11 state->last_inst_priv = state->prv;
12 #endif
13 }
14
15 static void commit_log_print_insn(state_t* state, reg_t pc, insn_t insn)
16 {
17 #ifdef RISCV_ENABLE_COMMITLOG
18 int32_t priv = state->last_inst_priv;
19 uint64_t mask = (insn.length() == 8 ? uint64_t(0) : (uint64_t(1) << (insn.length() * 8))) - 1;
20 if (state->log_reg_write.addr) {
21 fprintf(stderr, "%1d 0x%016" PRIx64 " (0x%08" PRIx64 ") %c%2" PRIu64 " 0x%016" PRIx64 "\n",
22 priv,
23 pc,
24 insn.bits() & mask,
25 state->log_reg_write.addr & 1 ? 'f' : 'x',
26 state->log_reg_write.addr >> 1,
27 state->log_reg_write.data);
28 } else {
29 fprintf(stderr, "%1d 0x%016" PRIx64 " (0x%08" PRIx64 ")\n", priv, pc, insn.bits() & mask);
30 }
31 state->log_reg_write.addr = 0;
32 #endif
33 }
34
35 inline void processor_t::update_histogram(reg_t pc)
36 {
37 #ifdef RISCV_ENABLE_HISTOGRAM
38 pc_histogram[pc]++;
39 #endif
40 }
41
42 static reg_t execute_insn(processor_t* p, reg_t pc, insn_fetch_t fetch)
43 {
44 commit_log_stash_privilege(p->get_state());
45 reg_t npc = fetch.func(p, fetch.insn, pc);
46 if (!invalid_pc(npc)) {
47 commit_log_print_insn(p->get_state(), pc, fetch.insn);
48 p->update_histogram(pc);
49 }
50 return npc;
51 }
52
53 // fetch/decode/execute loop
54 void processor_t::step(size_t n)
55 {
56 // TODO: We should really not call this function at all when halted, to avoid
57 // burning CPU.
58 if (single_step) {
59 halted = false;
60 n = 1;
61 }
62 if (halted) {
63 return;
64 }
65
66 while (run && n > 0) {
67 size_t instret = 0;
68 reg_t pc = state.pc;
69 mmu_t* _mmu = mmu;
70
71 #define advance_pc() \
72 if (unlikely(invalid_pc(pc))) { \
73 switch (pc) { \
74 case PC_SERIALIZE_BEFORE: state.serialized = true; break; \
75 case PC_SERIALIZE_AFTER: instret++; break; \
76 default: abort(); \
77 } \
78 pc = state.pc; \
79 break; \
80 } else { \
81 state.pc = pc; \
82 instret++; \
83 }
84
85 try
86 {
87 take_interrupt();
88
89 if (unlikely(debug))
90 {
91 while (instret < n)
92 {
93 insn_fetch_t fetch = mmu->load_insn(pc);
94 if (!state.serialized)
95 disasm(fetch.insn);
96 pc = execute_insn(this, pc, fetch);
97 advance_pc();
98 }
99 }
100 else while (instret < n)
101 {
102 size_t idx = _mmu->icache_index(pc);
103 auto ic_entry = _mmu->access_icache(pc);
104
105 #define ICACHE_ACCESS(i) { \
106 insn_fetch_t fetch = ic_entry->data; \
107 ic_entry++; \
108 pc = execute_insn(this, pc, fetch); \
109 if (i == mmu_t::ICACHE_ENTRIES-1) break; \
110 if (unlikely(ic_entry->tag != pc)) goto miss; \
111 if (unlikely(instret+1 == n)) break; \
112 instret++; \
113 state.pc = pc; \
114 }
115
116 switch (idx) {
117 #include "icache.h"
118 }
119
120 advance_pc();
121 continue;
122
123 miss:
124 advance_pc();
125 // refill I$ if it looks like there wasn't a taken branch
126 if (pc > (ic_entry-1)->tag && pc <= (ic_entry-1)->tag + MAX_INSN_LENGTH)
127 _mmu->refill_icache(pc, ic_entry);
128 }
129 }
130 catch(trap_t& t)
131 {
132 take_trap(t, pc);
133 n = instret;
134 }
135
136 state.minstret += instret;
137 n -= instret;
138 }
139
140 if (single_step) {
141 single_step = false;
142 halted = true;
143 }
144 }