📄 ms.c
字号:
/* * Copyright (C) 1996-1998 by the Board of Trustees * of Leland Stanford Junior University. * * This file is part of the SimOS distribution. * See LICENSE file for terms of the license. * */ /* * ms - The simulator component of MXS. * * Simulates a variety of architectures, from a statically * scheduled R3000-like processor, to a dynamically * scheduled processor with speculative execution. * * Jim Bennett * 1993, 1994, 1995 */#ifdef MIPSY_MXS#include <sys/types.h>#endif#include "ms.h"#ifdef MIPSY_MXS#include "cpu_state.h"#include "pcache.h"#include "cp0.h"#elseextern void ms_cache_callbacks (struct s_cpu_state *st);#endifint ms_writeback (struct s_cpu_state *st);void ms_issue (struct s_cpu_state *st);void ms_execute (struct s_cpu_state *st);void ms_ldst_dep (struct s_cpu_state *st);void ms_memory (struct s_cpu_state *st);void free_inst (struct s_cpu_state *st, int inum);#ifdef MIPSY_MXS /* * ms_cycle_once - Step the given processor forward by one * cycle. */void ms_cycle_once (struct s_cpu_state *st) { WorkList *w;/* Main loop. Each time around the loop we emulate a single CPU cycle, *//* so this should be coded as tightly as possible. */ IncStat(ST_RUN_CYCLES); st->work_cycle++; if (st->work_cycle >= TICK_COUNT) { /* Every 100,000 cycles, reset */ /* the work clock and put out */ /* a tick mark. */ st->work_cycle -= TICK_COUNT; st->work_ticks++;#if 0 if (st->icount == ((CPUState *)(st->mipsyPtr))->numInstructions) { /* No isntruction graded since last time - trouble */ ms_break (st, NULL, "TIMOUT"); } st->icount = ((CPUState *)(st->mipsyPtr))->numInstructions;#endif#ifdef PRINT_DOTS printf (".0x%x",st->grad.pc); fflush(stdout); if ((st->work_ticks & 0x3f) == 0) printf ("\n\r0x%x", st->grad.pc);#endif for (w=st->work_head; (w); w = w->next) w->cycle -= TICK_COUNT; }#ifdef BREAKPOINT if ((st->work_ticks == (cycbrk/TICK_COUNT)) && (st->work_cycle == (cycbrk % TICK_COUNT))) ms_break (st, NULL, "CYCBRK");#endif /* Perform the per cycle operations: Writeback, Fetch, Issue, */ /* Execute, and Memory access. */ ms_writeback (st); ms_fetch (st); ms_issue (st); ms_execute (st); ms_ldst_dep (st); /* Update load/store dependencies */ ms_memory (st); ms_graduate (st); ms_except (st); }#else /* * ms_cycle_loop - All set up, let the machine run */void ms_cycle_loop (struct s_cpu_state *st) { int watchdog; WorkList *w;/* Main loop. Each time around the loop we emulate a single CPU cycle, *//* so this should be coded as tightly as possible. */ watchdog = 0; while (1) {#ifdef DEBUG_CHECKS watchdog++; if (watchdog >= 1000) ms_break (st, NULL, "TIMOUT");#endif st->work_cycle++; if (st->work_cycle >= TICK_COUNT) { /* Every 100,000 cycles, reset */ /* the work clock and put out */ /* a tick mark. */ st->work_cycle -= TICK_COUNT; st->work_ticks++;#ifdef PRINT_DOTS printf (".0x%x",st->grad.pc); fflush(stdout); if ((st->work_ticks & 0x3f) == 0) printf ("\n\r0x%x", st->grad.pc);#endif for (w=st->work_head; (w); w = w->next) w->cycle -= TICK_COUNT; }#ifdef BREAKPOINT if ((st->work_ticks == (cycbrk/TICK_COUNT)) && (st->work_cycle == (cycbrk % TICK_COUNT))) ms_break (st, NULL, "CYCBRK");#endif /* Perform the per cycle operations: Writeback, Fetch, Issue, */ /* Execute, and Memory access. */ if (ms_writeback (st)) watchdog = 0; /* Reset watchdog timer */ /* every time some work */ /* is done. */ ms_fetch (st); ms_issue (st); ms_execute (st); ms_ldst_dep (st); /* Update load/store dependencies */ ms_memory (st); ms_graduate (st); ms_cache_callbacks (st);skip_to_end: ; } }#endif /* MIPSY_MXS */ /* * ms_writeback - WRITEBACK the results of previous operations, * and unlock registers. Handled by the work list * mechanism (see the routine reg_writeback, below, * for the actual operations). * * As an indicator for the watchdog timer, ms_writeback * returns the amount of work performed. */int ms_writeback (struct s_cpu_state *st) { int loop_counter; WorkList *w; w=st->work_head; st->wbacks = 0; loop_counter = 0; while ((w) && (st->work_cycle >= w->cycle)) { loop_counter++; /* Perform work list item if */ /* it is due this cycle. */ (*w->f)((void *)st, w->arg2); st->free_tail->next = w; st->free_tail = w; w = w->next; st->free_tail->next = NULL; if ((st->work_head = w) == NULL) st->work_tail = NULL; } return (loop_counter); } /* * update_excuse - Change the reason why a given register * is not available. */void update_excuse (void *st, void *ix) { int reg_ix = (int)ix >> 1; STX->reg_excuse[reg_ix] = STX->new_excuse[reg_ix]; STX->new_excuse[reg_ix] = ST_NO_EXCUSE; } /* * unstall_fetch - Clear a branch stall condition for a thread * */void unstall_fetch (void *st, void *ix) { THREAD *th; th = &STX->threads [(int)ix]; th->stall_branch = 0; UpdateStallFetch (th); } /* * finish_call - Complete call processing by unstalling the * fetch unit and writing back the link pointer */void finish_call (void *st, void *ix) { INST *ip; BrTREE *br; THREAD *th; int inum, branch_node; ip = (INST *)ix; inum = ip - STX->iwin; branch_node = STX->iwin_br_node[inum]; if (branch_node >= 0) { br = &STX->branch_tree [branch_node]; th = &STX->threads [br->thread]; th->stall_branch = 0; UpdateStallFetch (th); prune_branch (STX, branch_node); } if (ip->r1 == 0) free_inst(st,inum); else reg_writeback (STX, (void *)ip->r1); } /* * finish_branch - Complete branch processing by unstalling the * fetch unit and freeing the branch instruction */void finish_branch (void *st, void *ix) { INST *ip; BrTREE *br; THREAD *th; int inum, branch_node; ip = (INST *)ix; inum = ip - STX->iwin; branch_node = STX->iwin_br_node[inum]; if (branch_node >= 0) { br = &STX->branch_tree [branch_node]; th = &STX->threads [br->thread]; th->stall_branch = 0; UpdateStallFetch (th); prune_branch (STX, branch_node); } free_inst (STX, inum); } /* * free_spec_inst - Free up instruction slot belonging to a * speculated instruction. * * Just like free_inst, except that this instruction might * be on someone's dependents list, so we have to fix that. * Also, the register owned by this instruction needs to * be freed. */void free_spec_inst (struct s_cpu_state *st, int this_inst) { INST *ip; int i, j, inum, reg_ix; /* Update the dependent lists. */ ip = &st->iwin [this_inst]; if (ip->r2 > 0) { reg_ix = ip->r2 >> 1; if (this_inst == st->reg_otherhalf2 [reg_ix]) st->reg_otherhalf2 [reg_ix] = -1; inum = st->reg_owner [reg_ix << 1]; for (i=0, j=0; i<st->iwin_index2 [inum]; j++) { st->iwin_dep2 [inum][i] = st->iwin_dep2 [inum][j]; if (st->iwin_dep2 [inum][j] == this_inst) st->iwin_index2 [inum]--; else i++; } if (inum != st->reg_owner [(reg_ix << 1) + 1]) { inum = st->reg_owner [(reg_ix << 1) + 1]; for (i=0, j=0; i<st->iwin_index2 [inum]; j++) { st->iwin_dep2 [inum][i] = st->iwin_dep2 [inum][j]; if (st->iwin_dep2 [inum][j] == this_inst) st->iwin_index2 [inum]--; else i++; } } } if (ip->r3 > 0) { reg_ix = ip->r3 >> 1; if (this_inst == st->reg_otherhalf3 [reg_ix]) st->reg_otherhalf3 [reg_ix] = -1; inum = st->reg_owner [reg_ix << 1]; for (i=0, j=0; i<st->iwin_index3 [inum]; j++) { st->iwin_dep3 [inum][i] = st->iwin_dep3 [inum][j]; if (st->iwin_dep3 [inum][j] == this_inst) st->iwin_index3 [inum]--; else i++; } if (inum != st->reg_owner [(reg_ix << 1) + 1]) { inum = st->reg_owner [(reg_ix << 1) + 1]; for (i=0, j=0; i<st->iwin_index3 [inum]; j++) { st->iwin_dep3 [inum][i] = st->iwin_dep3 [inum][j]; if (st->iwin_dep3 [inum][j] == this_inst) st->iwin_index3 [inum]--; else i++; } } } /* Take care of the target (destination) register */ if (ip->r1 > 0) { /* Special registers get the full treatment, */ /* since they might have dependents that aren't */ /* descended from this thread. */ if (st->iwin_flags[this_inst] & IWIN_CTL) { reg_writeback ((void *)st, (void *)ip->r1); return; } else { REGSTAT *rs; reg_ix = ip->r1 >> 1; rs = &st->reg_rstat [reg_ix]; if (rs->reg_status & REG_DMAP) rs->reg_status &= ~REG_DMAP; else { rs->reg_status &= ~REG_IN_WIN; st->reg_excuse[reg_ix] = ST_NO_EXCUSE; st->new_excuse[reg_ix] = ST_NO_EXCUSE; CheckRegFree (st, rs, reg_ix); } } } /* Then free like a regular instruction. */ free_inst (st, this_inst); } /* * reg_writeback - Perform WRITEBACK actions: * * 1. Free slot of completing instruction * 2. Decrement ref counts of source registers for * completing instruction * 3. Clear dependent flags of instructions waiting for * this result * 4. Update register status (no longer "in window") * 5. Free load/store buffer entry of loads */void reg_writeback (void *st, void *ix) { int inum, inum2, dwi; int reg_ix = (int)ix >> 1; REGSTAT *rs; WorkDecls;#ifdef DEBUG_CHECKS if ((int)ix == 0) { fprintf(stderr, "r0 passed to reg_writeback\n"); ms_break(st, NULL, "r0write"); }#endif /* If we haven't exceeded allowed write backs, unlock */ /* the register. */ if (STX->wbacks < WBACK_WIDTH) {#ifdef BREAKPOINT if ((int)ix == trace_preg) printf ( "Register %d value = 0x%8.8x %12.6f %12.6f\r\n", (int)ix, STX->regs[(int)ix], *(float *)&STX->regs[(int)ix], *(double *)&STX->regs[(int)ix] );#endif STX->wbacks++; inum = STX->reg_owner [(int)ix]; rs = &STX->reg_rstat [reg_ix]; if (rs->reg_status & REG_DMAP) { rs->reg_status &= ~REG_DMAP; /* If this is the first half of a two */ /* instruction definition, turn off the */ /* dependent flags of the instruction */ /* that writes the other half of the */ /* register. */ inum2 = STX->reg_otherhalf2 [reg_ix]; if (inum2 >= 0) { STX->iwin_flags [inum2] &= ~IWIN_DEP2; CheckInstAvail (STX, inum2); } inum2 = STX->reg_otherhalf3 [reg_ix]; if (inum2 >= 0) { STX->iwin_flags [inum2] &= ~IWIN_DEP3; CheckInstAvail (STX, inum2); } /* Transfer dependent list to the other */ /* instruction that defines this register */ inum2 = STX->reg_owner [((int)ix) ^ 0x01]; STX->reg_owner [(int)ix] = inum2; for (dwi = 0; dwi < STX->iwin_index2[inum]; dwi++) STX->iwin_dep2 [inum2] [STX->iwin_index2[inum2]++] = STX->iwin_dep2 [inum] [dwi]; for (dwi = 0; dwi < STX->iwin_index3[inum]; dwi++) STX->iwin_dep3 [inum2] [STX->iwin_index3[inum2]++] = STX->iwin_dep3 [inum] [dwi]; } else { /* Turn off REG_IN_WIN status of this */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -