-
Notifications
You must be signed in to change notification settings - Fork 0
/
cpzero.cc
474 lines (429 loc) · 13.1 KB
/
cpzero.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
/* R3000 system control coprocessor emulation ("coprocessor zero") modified in associated with the pipeline simulation
Original work Copyright 2001, 2002, 2003 Brian R. Gaeke.
Modified work Copyright (c) 2021 Amano laboratory, Keio University.
Modifier: Takuya Kojima
This file is part of CubeSim, a cycle accurate simulator for 3-D stacked system.
It is derived from a source code of VMIPS project under GPLv2.
CubeSim is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
CubeSim is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CubeSim. If not, see <https://www.gnu.org/licenses/>.
*/
#include "cpzero.h"
#include "mapper.h"
#include "excnames.h"
#include "cpu.h"
#include "cpzeroreg.h"
#include "intctrl.h"
#include "deviceexc.h"
#include "error.h"
#include "vmips.h"
#include "options.h"
static uint32 read_masks[] = {
Index_MASK, Random_MASK, EntryLo_MASK, 0, Context_MASK,
PageMask_MASK, Wired_MASK, Error_MASK, BadVAddr_MASK, Count_MASK,
EntryHi_MASK, Compare_MASK, Status_MASK, Cause_MASK, EPC_MASK,
PRId_MASK, Config_MASK, LLAddr_MASK, WatchLo_MASK, WatchHi_MASK,
CPUID_MASK, 0, 0, 0, 0, 0, ECC_MASK, CacheErr_MASK, TagLo_MASK, TagHi_MASK,
ErrorEPC_MASK, 0
};
static uint32 write_masks[] = {
Index_MASK, 0, EntryLo_MASK, 0, Context_MASK & ~Context_BadVPN_MASK,
PageMask_MASK, Wired_MASK, Error_MASK, 0, Count_MASK,
EntryHi_MASK, Compare_MASK, Status_MASK,
Cause_MASK & ~Cause_IP_Ext_MASK, 0, 0, Config_MASK, LLAddr_MASK,
WatchLo_MASK, WatchHi_MASK, 0, 0, 0, 0, 0, 0, ECC_MASK,
CacheErr_MASK, TagLo_MASK, TagHi_MASK, ErrorEPC_MASK, 0
};
/* Reset (warm or cold) */
void
CPZero::reset(void)
{
int r;
for (r = 0; r < 16; r++) {
#ifdef INTENTIONAL_CONFUSION
reg[r] = random() & (read_masks[r] | write_masks[r]);
#endif /* INTENTIONAL_CONFUSION */
}
/* Turn off any randomly-set pending-interrupt bits, as these
* can impact correctness. */
reg[Cause] &= ~Cause_IP_MASK;
/* Reset Random register to upper bound (8<=Random<=63) */
reg[Random] = Random_UPPER_BOUND << 8;
/* Reset Status register: clear KUc, IEc, SwC (i.e., caches are not
* switched), TS (TLB shutdown has not occurred), and set
* BEV (Bootstrap exception vectors ARE in effect).
*/
reg[Status] = (reg[Status] | Status_DS_BEV_MASK) &
~(Status_KUc_MASK | Status_IEc_MASK | Status_DS_SwC_MASK |
Status_DS_TS_MASK);
reg[PRId] = 0x00000230; /* MIPS R3000A */
reg[CPUID] = cpuid;
}
/* Yow!! Are we in KERNEL MODE yet?? ...Read the Status register. */
bool
CPZero::kernel_mode(void) const
{
return !(reg[Status] & Status_KUc_MASK);
}
void
CPZero::dump_regs(FILE *f)
{
int x;
fprintf(f, "CP0 Dump Registers: [ ");
for (x = 0; x < 16; x++) {
fprintf(f," R%02d=%08x ",x,reg[x]);
if (x % 4 == 1) {
fputc('\n',f);
}
}
fprintf(f, "]\n");
}
static void
dump_tlb_entry(FILE *f, int index, const TLBEntry &e)
{
fprintf(f,"Entry %02d: (%08x%08x) V=%05x A=%02x P=%05x %c%c%c%c\n", index,
e.entryHi, e.entryLo, e.vpn()>>12, e.asid()>>6, e.pfn()>>12,
e.noncacheable()?'N':'n', e.dirty()?'D':'d',
e.valid()?'V':'v', e.global()?'G':'g');
}
void
CPZero::dump_tlb(FILE *f)
{
int x;
fprintf(f, "Dump TLB: [\n");
for (x = 0; x < TLB_ENTRIES; ++x)
dump_tlb_entry(f, x, tlb[x]);
fprintf(f, "]\n");
}
void
CPZero::dump_regs_and_tlb(FILE *f)
{
dump_regs(f);
dump_tlb(f);
}
/* Request for address translation (possibly using the TLB). */
uint32
CPZero::address_trans(uint32 vaddr, int mode, bool *cacheable,
DeviceExc *client)
{
if (kernel_mode()) {
switch(vaddr & KSEG_SELECT_MASK) {
case KSEG0:
*cacheable = true;
return vaddr;
case KSEG1:
*cacheable = false;
return vaddr;
case KSEG2:
case KSEG2_top:
return tlb_translate(KSEG2, vaddr, mode, cacheable, client);
default: /* KUSEG */
return tlb_translate(KUSEG, vaddr, mode, cacheable, client);
}
}
/* user mode */
if (vaddr & KERNEL_SPACE_MASK) {
/* Can't go there. */
client->exception(mode == DATASTORE ? AdES : AdEL, mode);
return 0xffffffff;
} else /* user space address */ {
return tlb_translate(KUSEG, vaddr, mode, cacheable, client);
}
}
void
CPZero::load_addr_trans_excp_info(uint32 va, uint32 vpn, TLBEntry *match)
{
reg[BadVAddr] = va;
reg[Context] = (reg[Context] & ~Context_BadVPN_MASK) | ((va & 0x7ffff000) >> 10);
reg[EntryHi] = (va & EntryHi_VPN_MASK) | (reg[EntryHi] & ~EntryHi_VPN_MASK);
}
int
CPZero::find_matching_tlb_entry(uint32 vpn, uint32 asid)
{
for (uint16 x = 0; x < TLB_ENTRIES; x++)
if (tlb[x].vpn() == vpn && (tlb[x].global() || tlb[x].asid() == asid))
return x;
return -1;
}
uint32
CPZero::tlb_translate(uint32 seg, uint32 vaddr, int mode, bool *cacheable,
DeviceExc *client)
{
uint32 asid = reg[EntryHi] & EntryHi_ASID_MASK;
uint32 vpn = vaddr & EntryHi_VPN_MASK;
int index = find_matching_tlb_entry(vpn, asid);
TLBEntry *match = (index == -1) ? 0 : &tlb[index];
tlb_miss_user = false;
if (match && match->valid()) {
if (mode == DATASTORE && !match->dirty()) {
/* TLB Mod exception - write to page not marked "dirty" */
load_addr_trans_excp_info(vaddr,vpn,match);
client->exception(Mod, DATASTORE);
return 0xffffffff;
} else {
/* We have a matching TLB entry which is valid. */
*cacheable = !match->noncacheable();
return match->pfn() | (vaddr & ~EntryHi_VPN_MASK);
}
}
/* If we got here, then there was no matching tlb entry, or it wasn't
* valid. Use special refill handler vector for user TLB miss. */
tlb_miss_user = (seg == KUSEG && !match);
load_addr_trans_excp_info(vaddr,vpn,match);
client->exception(mode == DATASTORE ? TLBS : TLBL, mode);
if (machine->opt->option("excmsg")->flag) {
/* If exception spew is on, print the fault address. It
* is just too handy to have. */
fprintf(stderr, " %s TLB miss at address 0x%x\n",
tlb_miss_user ? "User" : "Kernel", vaddr);
}
return 0xffffffff;
}
uint32
CPZero::read_reg(const uint16 r)
{
if (r == Cause) {
/* Update IP field of Cause register. */
reg[Cause] = (reg[Cause] & ~Cause_IP_MASK) | getIP();
}
/* This ensures that non-existent CP0 registers read as zero. */
return reg[r] & read_masks[r];
}
void
CPZero::write_reg(const uint16 r, const uint32 data)
{
/* This preserves the bits which are readable but not writable, and writes
* the bits which are writable with new data, thus making it suitable
* for mtc0-type operations. If you want to write all the bits which
* are _connected_, use: reg[r] = new_data & write_masks[r]; . */
reg[r] = (reg[r] & (read_masks[r] & ~write_masks[r]))
| (data & write_masks[r]);
}
void
CPZero::mfc0_emulate(PipelineRegs *preg)
{
//cpu->put_reg (CPU::rt (instr), read_reg (CPU::rd (instr)));
preg->result = read_reg(CPU::rd (preg->instr));
preg->dst = CPU::rt(preg->instr);
preg->w_reg_data = &(preg->result);
}
void
CPZero::mtc0_emulate(uint32 instr, uint32 pc)
{
// don't occur data hazard & no need of forwarding
// due to stalling of cpzreo op
write_reg (CPU::rd (instr), cpu->get_reg (CPU::rt (instr)));
}
void
CPZero::bc0x_emulate(uint32 instr, uint32 pc)
{
uint16 condition = CPU::rt (instr);
switch (condition) {
case 0: /* bc0f */ if (! cpCond ()) { cpu->branch (instr, pc); } break;
case 1: /* bc0t */ if (cpCond ()) { cpu->branch (instr, pc); } break;
case 2: /* bc0fl - not valid, but not reserved(A-17, H&K) - no-op. */ break;
case 3: /* bc0tl - not valid, but not reserved(A-21, H&K) - no-op. */ break;
default: cpu->exception (RI); break; /* reserved */
}
}
void
CPZero::tlbr_emulate(uint32 instr, uint32 pc)
{
reg[EntryHi] = (tlb[(reg[Index] & Index_Index_MASK) >> 8].entryHi) &
write_masks[EntryHi];
reg[EntryLo] = (tlb[(reg[Index] & Index_Index_MASK) >> 8].entryLo) &
write_masks[EntryLo];
}
void
CPZero::tlb_write(unsigned index)
{
tlb[index].entryHi = read_reg(EntryHi);
tlb[index].entryLo = read_reg(EntryLo);
}
void
CPZero::tlbwi_emulate(uint32 instr, uint32 pc)
{
tlb_write ((reg[Index] & Index_Index_MASK) >> 8);
}
void
CPZero::tlbwr_emulate(uint32 instr, uint32 pc)
{
tlb_write ((reg[Random] & Random_Random_MASK) >> 8);
}
void
CPZero::tlbp_emulate(uint32 instr, uint32 pc)
{
uint32 vpn = reg[EntryHi] & EntryHi_VPN_MASK;
uint32 asid = reg[EntryHi] & EntryHi_ASID_MASK;
int idx = find_matching_tlb_entry (vpn, asid);
if (idx != -1)
reg[Index] = (idx << 8);
else
reg[Index] = (1 << 31);
}
void
CPZero::rfe_emulate(uint32 instr, uint32 pc)
{
reg[Status] = (reg[Status] & 0xfffffff0) | ((reg[Status] >> 2) & 0x0f);
}
void
CPZero::cpzero_emulate(PipelineRegs *preg)
{
uint32 instr = preg->instr;
uint32 pc = preg->pc;
uint16 rs = CPU::rs (instr);
if (CPU::rs (instr) > 15) {
switch (CPU::funct (instr)) {
case 1: tlbr_emulate (instr, pc); break;
case 2: tlbwi_emulate (instr, pc); break;
case 6: tlbwr_emulate (instr, pc); break;
case 8: tlbp_emulate (instr, pc); break;
case 16: rfe_emulate (instr, pc); break;
default: cpu->exception (RI, ANY, 0); break;
}
} else {
switch (rs) {
case 0: mfc0_emulate (preg); break;
case 2: cpu->exception (RI, ANY, 0); break; /* cfc0 - reserved */
case 4: mtc0_emulate (instr, pc); break;
case 6: cpu->exception (RI, ANY, 0); break; /* ctc0 - reserved */
case 8: bc0x_emulate (instr,pc); break;
default: cpu->exception (RI, ANY, 0); break;
}
}
}
void
CPZero::adjust_random(void)
{
/* For initial == 12, lower bound == 8, upper bound == 63, the
* sequence looks like this:
* 12 11 10 9 8 63 62 61 60 ... 12 11 10 9 8 63 ... (x)
* 51 52 53 54 55 0 1 2 3 ... 51 52 53 54 55 0 ... (63 - x)
*/
int32 r = (int32) (reg[Random] >> 8);
r = -(((Random_UPPER_BOUND - r + 1) %
(Random_UPPER_BOUND - Random_LOWER_BOUND + 1)) -
Random_UPPER_BOUND);
reg[Random] = (uint32) (r << 8);
}
uint32
CPZero::getIP(void)
{
uint32 HwIP = 0, IP = 0;
if (intc != NULL) {
/* Check for a hardware interrupt. */
HwIP = intc->calculateIP();
}
IP = (reg[Cause] & Cause_IP_SW_MASK) | HwIP;
return IP;
}
void
CPZero::enter_exception(uint32 pc, uint32 excCode, uint32 ce, bool dly)
{
/* Save exception PC in EPC. */
reg[EPC] = pc;
/* Disable interrupts and enter Kernel mode. */
reg[Status] = (reg[Status] & ~Status_KU_IE_MASK) |
((reg[Status] & Status_KU_IE_MASK) << 2);
/* Clear Cause register BD, CE, and ExcCode fields. */
reg[Cause] &= ~(Cause_BD_MASK|Cause_CE_MASK|Cause_ExcCode_MASK);
/* Set Cause register CE field if this is a Coprocessor
* Unusable exception. (If we are passed ce=-1 we don't want
* to toggle bits in Cause.) */
if (excCode == CpU) {
reg[Cause] |= ((ce & 0x3) << 28);
}
/* Update IP, BD, ExcCode fields of Cause register. */
reg[Cause] &= ~Cause_IP_MASK;
reg[Cause] |= getIP () | (dly << 31) | (excCode << 2);
}
bool
CPZero::use_boot_excp_address(void)
{
return (reg[Status] & Status_DS_BEV_MASK);
}
bool
CPZero::caches_isolated(void)
{
return (reg[Status] & Status_DS_IsC_MASK);
}
bool
CPZero::caches_swapped(void)
{
return (reg[Status] & Status_DS_SwC_MASK);
}
bool
CPZero::cop_usable(int coprocno)
{
switch (coprocno) {
case 3: return (reg[Status] & Status_CU3_MASK);
case 2: return (reg[Status] & Status_CU2_MASK);
case 1: return (reg[Status] & Status_CU1_MASK);
case 0: return (reg[Status] & Status_CU0_MASK);
default: fatal_error ("Bad coprocno passed to CPZero::cop_usable()");
};
}
bool
CPZero::interrupts_enabled(void) const
{
return (reg[Status] & Status_IEc_MASK);
}
bool
CPZero::interrupt_pending(void)
{
if (! interrupts_enabled())
return false; /* Can't very well argue with IEc == 0... */
/* Mask IP with the interrupt mask, and return true if nonzero: */
return ((getIP () & (reg[Status] & Status_IM_MASK)) != 0);
}
void
CPZero::read_debug_info(uint32 *status, uint32 *bad, uint32 *cause)
{
*status = reg[Status];
*bad = reg[BadVAddr];
*cause = reg[Cause];
}
void
CPZero::write_debug_info(uint32 status, uint32 bad, uint32 cause)
{
reg[Status] = status;
reg[BadVAddr] = bad;
reg[Cause] = cause;
}
/* TLB translate VADDR without exceptions. Returns true if a valid
* TLB mapping is found, false otherwise. If VADDR has no valid mapping,
* PADDR is written with 0xffffffff, otherwise it is written with the
* translation.
*/
bool
CPZero::debug_tlb_translate(uint32 vaddr, uint32 *paddr)
{
bool rv = true;
if ((!kernel_mode()) && (vaddr & KERNEL_SPACE_MASK)) {
*paddr = 0xffffffff;
rv = false;
} else if (kernel_mode() && (vaddr & KSEG_SELECT_MASK) == KSEG0) {
*paddr = vaddr - KSEG0_CONST_TRANSLATION;
} else if (kernel_mode() && (vaddr & KSEG_SELECT_MASK) == KSEG1) {
*paddr = vaddr - KSEG1_CONST_TRANSLATION;
} else /* KUSEG */ {
uint32 asid = reg[EntryHi] & EntryHi_ASID_MASK;
uint32 vpn = vaddr & EntryHi_VPN_MASK;
int index = find_matching_tlb_entry (vpn, asid);
TLBEntry *match = (index == -1) ? 0 : &tlb[index];
if (!match || !match->valid()) {
*paddr = 0xffffffff;
rv = false;
} else {
*paddr = match->pfn() | (vaddr & ~EntryHi_VPN_MASK);
}
}
return rv;
}