Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | /* |
2 | * ========== Copyright Header Begin ========================================== | |
3 | * | |
4 | * OpenSPARC T2 Processor File: lockstep.c | |
5 | * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
6 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES. | |
7 | * | |
8 | * The above named program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public | |
10 | * License version 2 as published by the Free Software Foundation. | |
11 | * | |
12 | * The above named program is distributed in the hope that it will be | |
13 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public | |
18 | * License along with this work; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. | |
20 | * | |
21 | * ========== Copyright Header End ============================================ | |
22 | */ | |
23 | /* | |
24 | * Copyright 2006 Sun Microsystems, Inc. All rights reserved. | |
25 | * Use is subject to license terms. | |
26 | */ | |
27 | #pragma ident "@(#)lockstep.c 1.5 06/10/25 SMI" | |
28 | ||
29 | #include <stdio.h> | |
30 | #include <stdlib.h> | |
31 | #include <unistd.h> | |
32 | #include <sys/types.h> | |
33 | #include <sys/ipc.h> | |
34 | #include <sys/shm.h> | |
35 | #include <assert.h> | |
36 | #include <string.h> | |
37 | #include <strings.h> | |
38 | ||
39 | #include "ss_common.h" | |
40 | #include "niagara.h" | |
41 | ||
42 | #if 1 | |
43 | #define DBGP(s) do { s } while (0) | |
44 | #endif | |
45 | ||
46 | void dbg_lockstep(simcpu_t *sp, uint32_t rawi); | |
47 | void dbg_lockstep_parse(void); | |
48 | void dbg_lockstep_dump(void); | |
49 | ||
50 | static void dump_mismatch_data(simcpu_t *sp); | |
51 | static void dump_instn_data(simcpu_t *sp); | |
52 | static void dump_intregs(uint64_t *data); | |
53 | static void diff_intregs(uint64_t *intrega, uint64_t *intregb); | |
54 | ||
55 | #define MASTER 0xfeedface | |
56 | #define SLAVE 0xdeadbeef | |
57 | ||
58 | typedef char decoded_instn_str_t[256]; | |
59 | ||
60 | uint64_t start_instn = 0x0; | |
61 | bool_t trace_all = false; | |
62 | bool_t is_master = false; | |
63 | uint64_t current_instn = 0x0; | |
64 | ||
65 | typedef struct INSTN_DATA { | |
66 | uint64_t pc; | |
67 | uint64_t npc; | |
68 | uint64_t instn_cnt; | |
69 | uint32_t rawi; | |
70 | uint64_t intreg[NINT]; | |
71 | uint64_t cycle_target; | |
72 | } instn_data_t; | |
73 | ||
74 | typedef struct LOCKSTEP_DATA_T { | |
75 | int flag; | |
76 | instn_data_t prev_master; | |
77 | instn_data_t master; | |
78 | instn_data_t prev_slave; | |
79 | instn_data_t slave; | |
80 | } lockstep_data_t; | |
81 | ||
82 | #define SHM_ID (getuid() | ('F'<<24)) | |
83 | int shmfd; | |
84 | lockstep_data_t *ctrl_datap = NULL; | |
85 | ||
86 | /* | |
87 | * This will be called while parsing the conf file | |
88 | * to allow this hook to parse specific options from | |
89 | * the conf file. | |
90 | * | |
91 | * Format of lockstep directive is: | |
92 | * debug_hook [lockstep] [mode] [start] | |
93 | * | |
94 | * [lockstep] - we've already parsed this. That's how we got here. | |
95 | * [mode] - master or slave | |
96 | * master writes its data to shm region and waits for | |
97 | * slave to compare. Dump any mismatches when simulators | |
98 | * fall out of lockstep. | |
99 | * [start] - optional, when supplied we enter into lockstep mode | |
100 | * from this instn count onwards | |
101 | * | |
102 | * We need to create a shared mem region to store the lockstep | |
103 | * buffer so that the slave can see the %pc and reg state of the | |
104 | * master and compare against its %pc and reg state. | |
105 | */ | |
106 | void | |
107 | dbg_lockstep_parse(void) | |
108 | { | |
109 | lexer_tok_t tok; | |
110 | int shmflg; | |
111 | uint32_t shm_size = sizeof (lockstep_data_t); | |
112 | ||
113 | ||
114 | DBGP(printf("\nInside dbg_lockstep_parse()");); | |
115 | ||
116 | /* Parse mode */ | |
117 | tok = lex_get_token(); | |
118 | ||
119 | if (streq(lex.strp, "master")) | |
120 | is_master = true; | |
121 | else if (streq(lex.strp, "slave")) | |
122 | is_master = false; | |
123 | else | |
124 | lex_fatal("Unknown lockstep mode [%s]\n", lex.strp); | |
125 | ||
126 | /* | |
127 | * see if next option is a ';' - otherwise parse star_instn | |
128 | */ | |
129 | tok = lex_get_token(); | |
130 | if (tok == T_S_Colon) { | |
131 | trace_all = true; /* no start instn so check all instns */ | |
132 | lex_unget(); | |
133 | } else { | |
134 | lex_unget(); | |
135 | lex_get(T_Number); /* parse start */ | |
136 | start_instn = lex.val; | |
137 | ||
138 | trace_all = false; | |
139 | ||
140 | DBGP(printf("\nlockstep: start_instn=0x%llx", start_instn);); | |
141 | } | |
142 | ||
143 | /* | |
144 | * Setup shared mem segment | |
145 | */ | |
146 | if (is_master) | |
147 | shmflg = 0777 | IPC_CREAT; | |
148 | else | |
149 | shmflg = 0777; | |
150 | ||
151 | if ((shmfd = shmget(SHM_ID, shm_size, shmflg)) < 0) { | |
152 | perror("shmget"); | |
153 | exit(1); | |
154 | } | |
155 | ||
156 | ||
157 | if ((ctrl_datap = (lockstep_data_t *)shmat(shmfd, NULL, SHM_RND)) | |
158 | == (lockstep_data_t *)-1) { | |
159 | perror("shmat"); | |
160 | exit(1); | |
161 | } | |
162 | ||
163 | if (is_master) { | |
164 | memset(ctrl_datap, 0, sizeof (lockstep_data_t)); | |
165 | ctrl_datap->flag = MASTER; /* allow master to start */ | |
166 | } | |
167 | ||
168 | ||
169 | ||
170 | /* | |
171 | * return to parse_debug_hook() which will take care of | |
172 | * parsing the last semi colon. | |
173 | */ | |
174 | } | |
175 | ||
176 | /* | |
177 | * This function will get called before each instruction | |
178 | * gets executed in execloop(). | |
179 | */ | |
180 | void | |
181 | dbg_lockstep(simcpu_t *sp, uint32_t rawi) | |
182 | { | |
183 | sparcv9_cpu_t *v9p; | |
184 | ||
185 | current_instn++; | |
186 | ||
187 | /* Check to see if we are comparing instns yet */ | |
188 | if (trace_all == false) { | |
189 | if (sp->cycle < start_instn) | |
190 | return; | |
191 | } | |
192 | ||
193 | if (sp->cycle == start_instn) { | |
194 | printf("lockstep: Turned on lockstep mode on instn=0x%llx", | |
195 | start_instn); fflush(stdout); | |
196 | } | |
197 | ||
198 | while (1) { | |
199 | if (is_master) { | |
200 | /* | |
201 | * MASTER Loop, entered on every instruction | |
202 | */ | |
203 | if (ctrl_datap->flag == MASTER) { /* wait for flag to be set */ | |
204 | ||
205 | /* write master %pc etc. to shared mem */ | |
206 | ctrl_datap->master.pc = sp->pc; | |
207 | ctrl_datap->master.npc = sp->npc; | |
208 | ctrl_datap->master.instn_cnt = sp->total_instr; | |
209 | ctrl_datap->master.rawi = rawi; | |
210 | bcopy(sp->intreg, ctrl_datap->master.intreg, | |
211 | (sizeof (uint64_t)) * NINT); | |
212 | ctrl_datap->master.cycle_target = sp->cycle_target; | |
213 | ||
214 | /* Signal slave that master has written data */ | |
215 | ctrl_datap->flag = SLAVE; | |
216 | ||
217 | /* Hand cpu over to slave */ | |
218 | yield(); | |
219 | ||
220 | /* When cpu comes back, we return to exec_loop */ | |
221 | return; | |
222 | } /* if */ | |
223 | } else { | |
224 | /* | |
225 | * SLAVE Loop | |
226 | */ | |
227 | if (ctrl_datap->flag == SLAVE) { | |
228 | ||
229 | /* | |
230 | * Save slave data - so we can compare current data with | |
231 | * data from previous instn. | |
232 | */ | |
233 | ctrl_datap->slave.pc = sp->pc; | |
234 | ctrl_datap->slave.npc = sp->npc; | |
235 | ctrl_datap->slave.instn_cnt = sp->total_instr; | |
236 | bcopy(sp->intreg, ctrl_datap->slave.intreg, | |
237 | (sizeof (uint64_t)) * NINT); | |
238 | ctrl_datap->slave.rawi = rawi; | |
239 | ctrl_datap->slave.cycle_target = sp->cycle_target; | |
240 | ||
241 | /* | |
242 | * Compare Master instns and data with Slave instns | |
243 | * and data. If they don't match, we have a problem!! | |
244 | */ | |
245 | ||
246 | if (bcmp(&ctrl_datap->master, &ctrl_datap->slave, | |
247 | sizeof (instn_data_t)) != 0) { | |
248 | dump_mismatch_data(sp); | |
249 | exit(1); | |
250 | } | |
251 | ||
252 | /* Save master data to prev_master */ | |
253 | bcopy(&ctrl_datap->master, &ctrl_datap->prev_master, | |
254 | sizeof (instn_data_t)); | |
255 | ||
256 | /* Save slave data to prev_slave */ | |
257 | bcopy(&ctrl_datap->slave, &ctrl_datap->prev_slave, | |
258 | sizeof (instn_data_t)); | |
259 | ||
260 | /* Signal master that slave has written data */ | |
261 | ctrl_datap->flag = MASTER; | |
262 | ||
263 | /* Give up the cpu so master can do some work */ | |
264 | yield(); | |
265 | ||
266 | /* When cpu comes back, we return to exec_loop */ | |
267 | return; | |
268 | } /* if */ | |
269 | ||
270 | } /* else */ | |
271 | ||
272 | /* | |
273 | * Rather than have the MASTER legion spin in this while loop | |
274 | * while the SLAVE is comparing data, why not hand the cpu | |
275 | * over to the SLAVE process so it can complete it's work | |
276 | * and return the flag to the MASTER legion. | |
277 | */ | |
278 | yield(); | |
279 | } /* while */ | |
280 | } | |
281 | ||
282 | void | |
283 | dbg_lockstep_dump(void) | |
284 | { | |
285 | printf("\ndbg_lockstep_dump: mode=[%s] start=0x%llx instn_count=[0x%llx]", | |
286 | is_master ? "master" : "slave", start_instn, current_instn); | |
287 | } | |
288 | ||
289 | static void | |
290 | dump_intregs(uint64_t *intreg) | |
291 | { | |
292 | int i; | |
293 | ||
294 | for (i = 0; i < 8; i++) { | |
295 | printf("\ng%d=0x%016llx o%d=0x%016llx l%d=0x%016llx i%d=0x%016llx", | |
296 | i, intreg[i], | |
297 | i, intreg[i+8], | |
298 | i, intreg[i+16], | |
299 | i, intreg[i+24]); | |
300 | } | |
301 | } | |
302 | ||
303 | static void | |
304 | diff_intregs(uint64_t *intrega, uint64_t *intregb) | |
305 | { | |
306 | int i; | |
307 | ||
308 | for (i = 0; i < 8; i++) { | |
309 | if (intrega[i] != intregb[i]) { | |
310 | printf("\n --> g%d=0x%016llx != g%d=0x%016llx", | |
311 | i, intrega[i], i, intregb[i]); | |
312 | } | |
313 | if (intrega[i+8] != intregb[i+8]) { | |
314 | printf("\n --> o%d=0x%016llx != o%d=0x%016llx", | |
315 | i, intrega[i+8], i, intregb[i+8]); | |
316 | } | |
317 | if (intrega[i+16] != intregb[i+16]) { | |
318 | printf("\n --> l%d=0x%016llx != l%d=0x%016llx", | |
319 | i, intrega[i+16], i, intregb[i+16]); | |
320 | } | |
321 | if (intrega[i+24] != intregb[i+24]) { | |
322 | printf("\n --> i%d=0x%016llx != i%d=0x%016llx", | |
323 | i, intrega[i+24], i, intregb[i+24]); | |
324 | } | |
325 | } | |
326 | } | |
327 | ||
328 | static void | |
329 | dump_mismatch_data(simcpu_t *sp) | |
330 | { | |
331 | ||
332 | printf("\nERROR: Instn OR Data Mismatch !!!"); | |
333 | ||
334 | /* XXX FIXME: could dump out deubg_log if there is one here */ | |
335 | ||
336 | printf("\nMismatch Instn (has not been executed yet) marked with ->"); | |
337 | dump_instn_data(sp); | |
338 | ||
339 | printf("\nMaster registers:"); | |
340 | dump_intregs(ctrl_datap->master.intreg); | |
341 | ||
342 | printf("\nSlave registers:"); | |
343 | dump_intregs(sp->intreg); | |
344 | ||
345 | printf("\nDiffs:"); | |
346 | diff_intregs(ctrl_datap->master.intreg, sp->intreg); | |
347 | } | |
348 | ||
349 | static void | |
350 | dump_instn_data(simcpu_t *sp) | |
351 | { | |
352 | ||
353 | ||
354 | decoded_instn_str_t master_instn_str; | |
355 | decoded_instn_str_t prev_master_instn_str; | |
356 | decoded_instn_str_t slave_instn_str; | |
357 | decoded_instn_str_t prev_slave_instn_str; | |
358 | ||
359 | sparcv9_idis(master_instn_str, | |
360 | sizeof (decoded_instn_str_t), | |
361 | ctrl_datap->master.rawi, sp->pc); | |
362 | ||
363 | sparcv9_idis(prev_master_instn_str, | |
364 | sizeof (decoded_instn_str_t), | |
365 | ctrl_datap->prev_master.rawi, sp->pc); | |
366 | ||
367 | sparcv9_idis(slave_instn_str, | |
368 | sizeof (decoded_instn_str_t), | |
369 | ctrl_datap->slave.rawi, sp->pc); | |
370 | ||
371 | sparcv9_idis(prev_slave_instn_str, | |
372 | sizeof (decoded_instn_str_t), | |
373 | ctrl_datap->prev_slave.rawi, sp->pc); | |
374 | ||
375 | printf("\n" \ | |
376 | " master instn#[0x%llx] pc[0x%llx] rawi[0x%08x] npc[0x%llx] "\ | |
377 | "target[0x%llx] - [%s]\n" \ | |
378 | " slave instn#[0x%llx] pc[0x%llx] rawi[0x%08x] npc[0x%llx] "\ | |
379 | "target[0x%llx] - [%s]\n" \ | |
380 | "-> master instn#[0x%llx] pc[0x%llx] rawi[0x%08x] npc[0x%llx] "\ | |
381 | "target[0x%llx] - [%s]\n" \ | |
382 | "-> slave instn#[0x%llx] pc[0x%llx] rawi[0x%08x] npc[0x%llx] "\ | |
383 | "target[0x%llx] - [%s]", | |
384 | ||
385 | ctrl_datap->prev_master.instn_cnt, | |
386 | ctrl_datap->prev_master.pc, | |
387 | ctrl_datap->prev_master.rawi, | |
388 | ctrl_datap->prev_master.npc, | |
389 | ctrl_datap->prev_master.cycle_target, | |
390 | prev_master_instn_str, | |
391 | ||
392 | ctrl_datap->prev_slave.instn_cnt, | |
393 | ctrl_datap->prev_slave.pc, | |
394 | ctrl_datap->prev_slave.rawi, | |
395 | ctrl_datap->prev_slave.npc, | |
396 | ctrl_datap->prev_slave.cycle_target, | |
397 | prev_slave_instn_str, | |
398 | ||
399 | ctrl_datap->master.instn_cnt, | |
400 | ctrl_datap->master.pc, | |
401 | ctrl_datap->master.rawi, | |
402 | ctrl_datap->master.npc, | |
403 | ctrl_datap->master.cycle_target, | |
404 | master_instn_str, | |
405 | ||
406 | ctrl_datap->slave.instn_cnt, | |
407 | ctrl_datap->slave.pc, | |
408 | ctrl_datap->slave.rawi, | |
409 | ctrl_datap->slave.npc, | |
410 | ctrl_datap->slave.cycle_target, | |
411 | slave_instn_str); | |
412 | } |