Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / legion / src / procs / sunsparc / debug / lockstep.c
/*
* ========== Copyright Header Begin ==========================================
*
* OpenSPARC T2 Processor File: lockstep.c
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
*
* The above named program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License version 2 as published by the Free Software Foundation.
*
* The above named program 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 this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ========== Copyright Header End ============================================
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "@(#)lockstep.c 1.5 06/10/25 SMI"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <assert.h>
#include <string.h>
#include <strings.h>
#include "ss_common.h"
#include "niagara.h"
#if 1
#define DBGP(s) do { s } while (0)
#endif
void dbg_lockstep(simcpu_t *sp, uint32_t rawi);
void dbg_lockstep_parse(void);
void dbg_lockstep_dump(void);
static void dump_mismatch_data(simcpu_t *sp);
static void dump_instn_data(simcpu_t *sp);
static void dump_intregs(uint64_t *data);
static void diff_intregs(uint64_t *intrega, uint64_t *intregb);
#define MASTER 0xfeedface
#define SLAVE 0xdeadbeef
typedef char decoded_instn_str_t[256];
uint64_t start_instn = 0x0;
bool_t trace_all = false;
bool_t is_master = false;
uint64_t current_instn = 0x0;
typedef struct INSTN_DATA {
uint64_t pc;
uint64_t npc;
uint64_t instn_cnt;
uint32_t rawi;
uint64_t intreg[NINT];
uint64_t cycle_target;
} instn_data_t;
typedef struct LOCKSTEP_DATA_T {
int flag;
instn_data_t prev_master;
instn_data_t master;
instn_data_t prev_slave;
instn_data_t slave;
} lockstep_data_t;
#define SHM_ID (getuid() | ('F'<<24))
int shmfd;
lockstep_data_t *ctrl_datap = NULL;
/*
* This will be called while parsing the conf file
* to allow this hook to parse specific options from
* the conf file.
*
* Format of lockstep directive is:
* debug_hook [lockstep] [mode] [start]
*
* [lockstep] - we've already parsed this. That's how we got here.
* [mode] - master or slave
* master writes its data to shm region and waits for
* slave to compare. Dump any mismatches when simulators
* fall out of lockstep.
* [start] - optional, when supplied we enter into lockstep mode
* from this instn count onwards
*
* We need to create a shared mem region to store the lockstep
* buffer so that the slave can see the %pc and reg state of the
* master and compare against its %pc and reg state.
*/
void
dbg_lockstep_parse(void)
{
lexer_tok_t tok;
int shmflg;
uint32_t shm_size = sizeof (lockstep_data_t);
DBGP(printf("\nInside dbg_lockstep_parse()"););
/* Parse mode */
tok = lex_get_token();
if (streq(lex.strp, "master"))
is_master = true;
else if (streq(lex.strp, "slave"))
is_master = false;
else
lex_fatal("Unknown lockstep mode [%s]\n", lex.strp);
/*
* see if next option is a ';' - otherwise parse star_instn
*/
tok = lex_get_token();
if (tok == T_S_Colon) {
trace_all = true; /* no start instn so check all instns */
lex_unget();
} else {
lex_unget();
lex_get(T_Number); /* parse start */
start_instn = lex.val;
trace_all = false;
DBGP(printf("\nlockstep: start_instn=0x%llx", start_instn););
}
/*
* Setup shared mem segment
*/
if (is_master)
shmflg = 0777 | IPC_CREAT;
else
shmflg = 0777;
if ((shmfd = shmget(SHM_ID, shm_size, shmflg)) < 0) {
perror("shmget");
exit(1);
}
if ((ctrl_datap = (lockstep_data_t *)shmat(shmfd, NULL, SHM_RND))
== (lockstep_data_t *)-1) {
perror("shmat");
exit(1);
}
if (is_master) {
memset(ctrl_datap, 0, sizeof (lockstep_data_t));
ctrl_datap->flag = MASTER; /* allow master to start */
}
/*
* return to parse_debug_hook() which will take care of
* parsing the last semi colon.
*/
}
/*
* This function will get called before each instruction
* gets executed in execloop().
*/
void
dbg_lockstep(simcpu_t *sp, uint32_t rawi)
{
sparcv9_cpu_t *v9p;
current_instn++;
/* Check to see if we are comparing instns yet */
if (trace_all == false) {
if (sp->cycle < start_instn)
return;
}
if (sp->cycle == start_instn) {
printf("lockstep: Turned on lockstep mode on instn=0x%llx",
start_instn); fflush(stdout);
}
while (1) {
if (is_master) {
/*
* MASTER Loop, entered on every instruction
*/
if (ctrl_datap->flag == MASTER) { /* wait for flag to be set */
/* write master %pc etc. to shared mem */
ctrl_datap->master.pc = sp->pc;
ctrl_datap->master.npc = sp->npc;
ctrl_datap->master.instn_cnt = sp->total_instr;
ctrl_datap->master.rawi = rawi;
bcopy(sp->intreg, ctrl_datap->master.intreg,
(sizeof (uint64_t)) * NINT);
ctrl_datap->master.cycle_target = sp->cycle_target;
/* Signal slave that master has written data */
ctrl_datap->flag = SLAVE;
/* Hand cpu over to slave */
yield();
/* When cpu comes back, we return to exec_loop */
return;
} /* if */
} else {
/*
* SLAVE Loop
*/
if (ctrl_datap->flag == SLAVE) {
/*
* Save slave data - so we can compare current data with
* data from previous instn.
*/
ctrl_datap->slave.pc = sp->pc;
ctrl_datap->slave.npc = sp->npc;
ctrl_datap->slave.instn_cnt = sp->total_instr;
bcopy(sp->intreg, ctrl_datap->slave.intreg,
(sizeof (uint64_t)) * NINT);
ctrl_datap->slave.rawi = rawi;
ctrl_datap->slave.cycle_target = sp->cycle_target;
/*
* Compare Master instns and data with Slave instns
* and data. If they don't match, we have a problem!!
*/
if (bcmp(&ctrl_datap->master, &ctrl_datap->slave,
sizeof (instn_data_t)) != 0) {
dump_mismatch_data(sp);
exit(1);
}
/* Save master data to prev_master */
bcopy(&ctrl_datap->master, &ctrl_datap->prev_master,
sizeof (instn_data_t));
/* Save slave data to prev_slave */
bcopy(&ctrl_datap->slave, &ctrl_datap->prev_slave,
sizeof (instn_data_t));
/* Signal master that slave has written data */
ctrl_datap->flag = MASTER;
/* Give up the cpu so master can do some work */
yield();
/* When cpu comes back, we return to exec_loop */
return;
} /* if */
} /* else */
/*
* Rather than have the MASTER legion spin in this while loop
* while the SLAVE is comparing data, why not hand the cpu
* over to the SLAVE process so it can complete it's work
* and return the flag to the MASTER legion.
*/
yield();
} /* while */
}
void
dbg_lockstep_dump(void)
{
printf("\ndbg_lockstep_dump: mode=[%s] start=0x%llx instn_count=[0x%llx]",
is_master ? "master" : "slave", start_instn, current_instn);
}
static void
dump_intregs(uint64_t *intreg)
{
int i;
for (i = 0; i < 8; i++) {
printf("\ng%d=0x%016llx o%d=0x%016llx l%d=0x%016llx i%d=0x%016llx",
i, intreg[i],
i, intreg[i+8],
i, intreg[i+16],
i, intreg[i+24]);
}
}
static void
diff_intregs(uint64_t *intrega, uint64_t *intregb)
{
int i;
for (i = 0; i < 8; i++) {
if (intrega[i] != intregb[i]) {
printf("\n --> g%d=0x%016llx != g%d=0x%016llx",
i, intrega[i], i, intregb[i]);
}
if (intrega[i+8] != intregb[i+8]) {
printf("\n --> o%d=0x%016llx != o%d=0x%016llx",
i, intrega[i+8], i, intregb[i+8]);
}
if (intrega[i+16] != intregb[i+16]) {
printf("\n --> l%d=0x%016llx != l%d=0x%016llx",
i, intrega[i+16], i, intregb[i+16]);
}
if (intrega[i+24] != intregb[i+24]) {
printf("\n --> i%d=0x%016llx != i%d=0x%016llx",
i, intrega[i+24], i, intregb[i+24]);
}
}
}
static void
dump_mismatch_data(simcpu_t *sp)
{
printf("\nERROR: Instn OR Data Mismatch !!!");
/* XXX FIXME: could dump out deubg_log if there is one here */
printf("\nMismatch Instn (has not been executed yet) marked with ->");
dump_instn_data(sp);
printf("\nMaster registers:");
dump_intregs(ctrl_datap->master.intreg);
printf("\nSlave registers:");
dump_intregs(sp->intreg);
printf("\nDiffs:");
diff_intregs(ctrl_datap->master.intreg, sp->intreg);
}
static void
dump_instn_data(simcpu_t *sp)
{
decoded_instn_str_t master_instn_str;
decoded_instn_str_t prev_master_instn_str;
decoded_instn_str_t slave_instn_str;
decoded_instn_str_t prev_slave_instn_str;
sparcv9_idis(master_instn_str,
sizeof (decoded_instn_str_t),
ctrl_datap->master.rawi, sp->pc);
sparcv9_idis(prev_master_instn_str,
sizeof (decoded_instn_str_t),
ctrl_datap->prev_master.rawi, sp->pc);
sparcv9_idis(slave_instn_str,
sizeof (decoded_instn_str_t),
ctrl_datap->slave.rawi, sp->pc);
sparcv9_idis(prev_slave_instn_str,
sizeof (decoded_instn_str_t),
ctrl_datap->prev_slave.rawi, sp->pc);
printf("\n" \
" master instn#[0x%llx] pc[0x%llx] rawi[0x%08x] npc[0x%llx] "\
"target[0x%llx] - [%s]\n" \
" slave instn#[0x%llx] pc[0x%llx] rawi[0x%08x] npc[0x%llx] "\
"target[0x%llx] - [%s]\n" \
"-> master instn#[0x%llx] pc[0x%llx] rawi[0x%08x] npc[0x%llx] "\
"target[0x%llx] - [%s]\n" \
"-> slave instn#[0x%llx] pc[0x%llx] rawi[0x%08x] npc[0x%llx] "\
"target[0x%llx] - [%s]",
ctrl_datap->prev_master.instn_cnt,
ctrl_datap->prev_master.pc,
ctrl_datap->prev_master.rawi,
ctrl_datap->prev_master.npc,
ctrl_datap->prev_master.cycle_target,
prev_master_instn_str,
ctrl_datap->prev_slave.instn_cnt,
ctrl_datap->prev_slave.pc,
ctrl_datap->prev_slave.rawi,
ctrl_datap->prev_slave.npc,
ctrl_datap->prev_slave.cycle_target,
prev_slave_instn_str,
ctrl_datap->master.instn_cnt,
ctrl_datap->master.pc,
ctrl_datap->master.rawi,
ctrl_datap->master.npc,
ctrl_datap->master.cycle_target,
master_instn_str,
ctrl_datap->slave.instn_cnt,
ctrl_datap->slave.pc,
ctrl_datap->slave.rawi,
ctrl_datap->slave.npc,
ctrl_datap->slave.cycle_target,
slave_instn_str);
}