Remove dependence on binutils
[riscv-isa-sim.git] / riscv / processor.cc
1 #include "processor.h"
2 #include "common.h"
3 #include "config.h"
4 #include "sim.h"
5 #include "disasm.h"
6 #include <cmath>
7 #include <cstdlib>
8 #include <iostream>
9 #include <assert.h>
10
11 processor_t::processor_t(sim_t* _sim, mmu_t* _mmu, uint32_t _id)
12 : sim(*_sim), mmu(*_mmu), id(_id), utidx(0)
13 {
14 reset();
15
16 // create microthreads
17 for (int i=0; i<MAX_UTS; i++)
18 uts[i] = new processor_t(&sim, &mmu, id, i);
19 }
20
21 processor_t::processor_t(sim_t* _sim, mmu_t* _mmu, uint32_t _id,
22 uint32_t _utidx)
23 : sim(*_sim), mmu(*_mmu), id(_id), utidx(_utidx)
24 {
25 reset();
26 set_sr(sr | SR_EF | SR_EV);
27
28 // microthreads don't possess their own microthreads
29 for (int i=0; i<MAX_UTS; i++)
30 uts[i] = NULL;
31 }
32
33 processor_t::~processor_t()
34 {
35 }
36
37 void processor_t::reset()
38 {
39 run = false;
40
41 // the ISA guarantees on boot that the PC is 0x2000 and the the processor
42 // is in supervisor mode, and in 64-bit mode, if supported, with traps
43 // and virtual memory disabled. we accomplish this by setting EVEC to
44 // 0x2000 and *enabling* traps, then sending the core an IPI.
45 set_sr(SR_S | SR_SX | SR_ET | SR_IM);
46 evec = 0x2000;
47
48 // the following state is undefined upon boot-up,
49 // but we zero it for determinism
50 memset(XPR,0,sizeof(XPR));
51 memset(FPR,0,sizeof(FPR));
52
53 pc = 0;
54 epc = 0;
55 badvaddr = 0;
56 cause = 0;
57 pcr_k0 = 0;
58 pcr_k1 = 0;
59 count = 0;
60 compare = 0;
61 cycle = 0;
62 set_fsr(0);
63
64 // vector stuff
65 vecbanks = 0xff;
66 vecbanks_count = 8;
67 utidx = -1;
68 vlmax = 32;
69 vl = 0;
70 nxfpr_bank = 256;
71 nxpr_use = 32;
72 nfpr_use = 32;
73 }
74
75 void processor_t::set_sr(uint32_t val)
76 {
77 sr = val & ~SR_ZERO; // clear SR bits that read as zero
78
79 #ifndef RISCV_ENABLE_64BIT
80 sr &= ~(SR_SX | SR_UX); // SX=UX=0 for RV32 implementations
81 #endif
82 #ifndef RISCV_ENABLE_FPU
83 sr &= ~SR_EF;
84 #endif
85 #ifndef RISCV_ENABLE_RVC
86 sr &= ~SR_EC;
87 #endif
88 #ifndef RISCV_ENABLE_VEC
89 sr &= ~SR_EV;
90 #endif
91
92 // update MMU state and flush TLB
93 mmu.set_vm_enabled(sr & SR_VM);
94 mmu.set_supervisor(sr & SR_S);
95 mmu.flush_tlb();
96
97 // set the fixed-point register length
98 xprlen = ((sr & SR_S) ? (sr & SR_SX) : (sr & SR_UX)) ? 64 : 32;
99 }
100
101 void processor_t::set_fsr(uint32_t val)
102 {
103 fsr = val & ~FSR_ZERO; // clear FSR bits that read as zero
104 }
105
106 void processor_t::vcfg()
107 {
108 if (nxpr_use + nfpr_use < 2)
109 vlmax = nxfpr_bank * vecbanks_count;
110 else
111 vlmax = (nxfpr_bank / (nxpr_use + nfpr_use - 1)) * vecbanks_count;
112
113 vlmax = std::min(vlmax, MAX_UTS);
114 }
115
116 void processor_t::setvl(int vlapp)
117 {
118 vl = std::min(vlmax, vlapp);
119 }
120
121 void processor_t::take_interrupt()
122 {
123 uint32_t interrupts = interrupts_pending;
124 interrupts &= (sr & SR_IM) >> SR_IM_SHIFT;
125
126 if(interrupts && (sr & SR_ET))
127 for(int i = 0; ; i++, interrupts >>= 1)
128 if(interrupts & 1)
129 throw (trap_t)(trap_irq0 + i);
130 }
131
132 void processor_t::step(size_t n, bool noisy)
133 {
134 if(!run)
135 return;
136
137 size_t i = 0;
138 while(1) try
139 {
140 take_interrupt();
141
142 mmu_t& _mmu = mmu;
143 insn_t insn;
144 insn_func_t func;
145 reg_t npc = pc;
146
147 // execute_insn fetches and executes one instruction
148 #define execute_insn(noisy) \
149 do { \
150 insn = _mmu.load_insn(npc, sr & SR_EC, &func); \
151 if(noisy) disasm(insn,pc); \
152 npc = func(this, insn, npc); \
153 pc = npc; \
154 } while(0)
155
156 if(noisy) for( ; i < n; i++) // print out instructions as we go
157 execute_insn(true);
158 else
159 {
160 // unrolled for speed
161 for( ; n > 3 && i < n-3; i+=4)
162 {
163 execute_insn(false);
164 execute_insn(false);
165 execute_insn(false);
166 execute_insn(false);
167 }
168 for( ; i < n; i++)
169 execute_insn(false);
170 }
171
172 break;
173 }
174 catch(trap_t t)
175 {
176 // an exception occurred in the target processor
177 i++;
178 take_trap(t,noisy);
179 }
180 catch(vt_command_t cmd)
181 {
182 // this microthread has finished
183 i++;
184 assert(cmd == vt_command_stop);
185 break;
186 }
187 catch(halt_t t)
188 {
189 // sleep until IPI
190 reset();
191 return;
192 }
193
194 cycle += i;
195
196 // update timer and possibly register a timer interrupt
197 uint32_t old_count = count;
198 count += i;
199 if(old_count < compare && uint64_t(old_count) + i >= compare)
200 interrupts_pending |= 1 << TIMER_IRQ;
201 }
202
203 void processor_t::take_trap(trap_t t, bool noisy)
204 {
205 if(noisy)
206 printf("core %3d: trap %s, pc 0x%016llx\n",
207 id, trap_name(t), (unsigned long long)pc);
208
209 // switch to supervisor, set previous supervisor bit, disable traps
210 set_sr((((sr & ~SR_ET) | SR_S) & ~SR_PS) | ((sr & SR_S) ? SR_PS : 0));
211 cause = t;
212 epc = pc;
213 pc = evec;
214 badvaddr = mmu.get_badvaddr();
215 }
216
217 void processor_t::deliver_ipi()
218 {
219 interrupts_pending |= 1 << IPI_IRQ;
220 run = true;
221 }
222
223 void processor_t::disasm(insn_t insn, reg_t pc)
224 {
225 // the disassembler is stateless, so we share it
226 static disassembler disasm;
227 printf("core %3d: 0x%016llx (0x%08x) %s\n", id, (unsigned long long)pc,
228 insn.bits, disasm.disassemble(insn).c_str());
229 }