/** * University of Illinois/NCSA * Open Source License * * Copyright (c) 2007-2008,The Board of Trustees of the University of * Illinois. All rights reserved. * * Copyright (c) 2009 Sam King * * Developed by: * * Professor Sam King in the Department of Computer Science * The University of Illinois at Urbana-Champaign * http://www.cs.uiuc.edu/homes/kingst/Research.html * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated * documentation files (the "Software"), to deal with the * Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, * sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject * to the following conditions: * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimers. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimers in the documentation and/or other * materials provided with the distribution. * * Neither the names of Sam King, the University of Illinois, * nor the names of its contributors may be used to endorse * or promote products derived from this Software without * specific prior written permission. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS WITH THE SOFTWARE. */ #include <stdlib.h> #include <assert.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> #include <time.h> #include <string.h> #include <iostream> #include <fstream> #include <string> #include <sstream> using namespace std; #define RAM_SIZE 0x20000 #define NUM_REGS 32 #define OP_FORMAT_1 0x1 #define OP_FORMAT_2 0x0 #define OP_FORMAT_3_ALU 0x2 #define OP_FORMAT_3_MEM 0x3 #define OP2_BRANCH 0x2 #define OP2_SETHI 0x4 #define OP3_ADD 0x0 #define OP3_SUB 0x4 #define OP3_SUBCC 0x14 #define OP3_JMPL 0x38 #define OP3_LD 0x0 #define OP3_ST 0x4 #define COND_BNE 0x9 #define COND_B 0x8 #define COND_BE 0x1 int PRINT_INST = 1; #define COUT(x) do { if(PRINT_INST) { x; } } while(0) typedef uint32_t u32; typedef int32_t i32; struct CPU { u32 regs[NUM_REGS]; u32 pc; bool icc_z; bool icc_n; }; struct control_signals { u32 op; u32 rd; u32 cond; u32 op2; u32 op3; u32 rs1; u32 i; u32 asi; i32 simm; u32 imm; u32 disp22; u32 disp30; u32 rs2; u32 raw; }; static uint8_t *ram = NULL; static bool userQuit = false; static time_t startTime; void ctrlC(int /*signo*/) { userQuit = true; } u32 fetch(struct CPU *cpu) { u32 opcode; assert((cpu->pc & 0x3) == 0); assert(cpu->pc < RAM_SIZE); memcpy(&opcode, ram+cpu->pc, sizeof(opcode)); return opcode; } i32 sign_extend_22(u32 disp22) { i32 ret; ret = disp22; assert((disp22 & 0xffc00000) == 0); if((disp22 & 0x200000) != 0) { ret |= 0xffc00000; } return ret; } i32 sign_extend_13(u32 imm) { i32 ret; ret = imm; assert((imm & 0xffffe000) == 0); if((imm & 0x01000) != 0) { ret |= 0xffffe000; } return ret; } void decode_control_signals(u32 opcode, struct control_signals *control) { control->raw = opcode; control->op = (opcode >> 30) & 0x3; control->rd = (opcode >> 25) & 0x1f; control->op2 = (opcode >> 22) & 0x7; control->op3 = (opcode >> 19) & 0x3f; control->rs1 = (opcode >> 14) & 0x1f; control->i = (opcode >> 13) & 0x1; control->asi = (opcode >> 5) & 0xff; control->simm = sign_extend_13(opcode & 0x1fff); control->imm = opcode & 0x3fffff; control->rs2 = opcode & 0x1f; control->disp22 = opcode & 0x3fffff; control->disp30 = opcode & 0x3fffffff; control->cond = (opcode >> 25) & 0xf; } void set_icc(struct CPU *cpu, u32 value) { cpu->icc_n = (value & 0x80000000) == 0x80000000; cpu->icc_z = value == 0; } void add(struct CPU *cpu, u32 rd, u32 rs1, u32 rs2) { COUT(cout << "add r" << rs1 << ", r" << rs2 << ", r" << rd << endl); cpu->regs[rd] = cpu->regs[rs1] + cpu->regs[rs2]; } void addi(struct CPU *cpu, u32 rd, u32 rs1, i32 signedImm) { COUT(cout << "addi " << "r" << rs1 << ", " << signedImm << ", r" << rd << endl); cpu->regs[rd] = cpu->regs[rs1] + signedImm; } void subi(struct CPU *cpu, u32 rd, u32 rs1, i32 signedImm) { COUT(cout << "subi " << "r" << rs1 << ", " << signedImm << ", r" << rd << endl); cpu->regs[rd] = cpu->regs[rs1] - signedImm; } void subicc(struct CPU *cpu, u32 rd, u32 rs1, i32 signedImm) { COUT(cout << "subicc " << "r" << rs1 << ", " << signedImm << ", r" << rd << endl); cpu->regs[rd] = cpu->regs[rs1] - signedImm; set_icc(cpu, cpu->regs[rd]); } void sethi(struct CPU *cpu, u32 rd, u32 imm) { COUT(cout << "sethi " << imm << ", r" << rd << endl); cpu->regs[rd] = imm << 10; } void ld(struct CPU *cpu, u32 rd, u32 rs1, i32 simm) { COUT(cout << "ld [r" << rs1 << "+" << simm << "], r" << rd << endl); u32 guestAddr = cpu->regs[rs1] + simm; assert(guestAddr < RAM_SIZE); u32 *addr = (u32 *) (ram + guestAddr); cpu->regs[rd] = *addr; } void st(struct CPU *cpu, u32 rd, u32 rs1, i32 simm) { COUT(cout << "st r" << rd << ", [r" << rs1 << "+" << simm << "]" << endl); u32 guestAddr = cpu->regs[rs1] + simm; assert(guestAddr < RAM_SIZE); u32 *addr = (u32 *) (ram + guestAddr); *addr = cpu->regs[rd]; if(guestAddr == (RAM_SIZE - 4)) { cout << cpu->regs[rd] << endl; if(cpu->regs[rd] == 3524578) { cout << "totalTime = " << time(NULL) - startTime << endl; userQuit = true; } } } void bne(struct CPU *cpu, u32 disp22) { COUT(cout << "bne " << disp22 << " " << sign_extend_22(disp22) << endl); if(!cpu->icc_z) { cpu->pc = cpu->pc + (4 * sign_extend_22(disp22)); } } void be(struct CPU *cpu, u32 disp22) { COUT(cout << "be " << disp22 << " " << sign_extend_22(disp22) << endl); if(cpu->icc_z) { cpu->pc = cpu->pc + (4 * sign_extend_22(disp22)); } } void b(struct CPU *cpu, u32 disp22) { COUT(cout << "b " << disp22 << " " << sign_extend_22(disp22) << endl); cpu->pc = cpu->pc + (4 * sign_extend_22(disp22)); } void jmpli(struct CPU *cpu, u32 rd, u32 rs1, i32 simm) { COUT(cout << "jmpl r" << rs1 << ", " << simm << ", r" << rd << endl); cpu->regs[rd] = cpu->pc; cpu->pc = cpu->regs[rs1] + simm; } void call(struct CPU *cpu, u32 disp30) { COUT(cout << "call " << disp30 << endl); cpu->regs[15] = cpu->pc; cpu->pc = cpu->pc + (4 * disp30); } void unknown_inst(struct CPU *cpu, struct control_signals *control) { cout << "op = 0x" << hex << control->op << endl; cout << "op2 = 0x" << hex << control->op2 << endl; cout << "op3 = 0x" << hex << control->op3 << endl; cout << "pc = 0x" << hex << cpu->pc << endl; assert(false); } void process_format_1(struct CPU *cpu, struct control_signals *control) { assert(control->op == OP_FORMAT_1); call(cpu, control->disp30); } void process_format_2(struct CPU *cpu, struct control_signals *control) { assert(control->op == OP_FORMAT_2); switch(control->op2) { case OP2_BRANCH: switch(control->cond) { case COND_B: b(cpu, control->disp22); break; case COND_BE: be(cpu, control->disp22); break; case COND_BNE: bne(cpu, control->disp22); break; default: unknown_inst(cpu, control); } break; case OP2_SETHI: sethi(cpu, control->rd, control->imm); break; default: unknown_inst(cpu, control); } } void process_format_3_alu(struct CPU *cpu, struct control_signals *control) { assert(control->op == OP_FORMAT_3_ALU); switch(control->op3) { case OP3_ADD: if(control->i == 0) { add(cpu, control->rd, control->rs1, control->rs2); } else { addi(cpu, control->rd, control->rs1, control->simm); } break; case OP3_SUB: assert(control->i == 1); subi(cpu, control->rd, control->rs1, control->simm); break; case OP3_SUBCC: assert(control->i == 1); subicc(cpu, control->rd, control->rs1, control->simm); break; case OP3_JMPL: assert(control->i == 1); jmpli(cpu, control->rd, control->rs1, control->simm); break; default: unknown_inst(cpu, control); } } void process_format_3_mem(struct CPU *cpu, struct control_signals *control) { assert(control->op == OP_FORMAT_3_MEM); switch(control->op3) { case OP3_LD: assert(control->i == 1); ld(cpu, control->rd, control->rs1, control->simm); break; case OP3_ST: assert(control->i == 1); st(cpu, control->rd, control->rs1, control->simm); break; default: unknown_inst(cpu, control); } } bool execute(struct CPU *cpu, struct control_signals *control) { u32 savedPc = cpu->pc; COUT(cout << "0x" << hex << cpu->pc << dec << " "); // writes to reg 0 are ignored and reads should always be 0 cpu->regs[0] = 0; switch (control->op) { case OP_FORMAT_1: process_format_1(cpu, control); break; case OP_FORMAT_2: process_format_2(cpu, control); break; case OP_FORMAT_3_ALU: process_format_3_alu(cpu, control); break; case OP_FORMAT_3_MEM: process_format_3_mem(cpu, control); break; default: unknown_inst(cpu, control); } // the pc is modified in the loop after the branch instruction if(cpu->pc == savedPc) { cpu->pc += sizeof(cpu->pc); if(cpu->pc < savedPc) { return false; } } return true; } string prompt(const char *str) { string ret; cout << str; cout.flush(); cin >> ret; return ret; } void fillState(char *fileName, void *buf, int size) { int ret, fd; assert(size > 0); fd = open(fileName, O_RDONLY); assert(fd >= 0); ret = read(fd, buf, size); assert((ret == size) && (ret>0)); close(fd); } void saveState(const char *fileName, void *buf, int size) { int ret, fd; assert(size > 0); fd = open(fileName, O_WRONLY | O_TRUNC | O_CREAT, 0644); assert(fd >= 0); ret = write(fd, buf, size); assert(ret == size); close(fd); } void cpu_exec(struct CPU *cpu) { struct control_signals *control = new struct control_signals; u32 opcode; bool runSimulation = true; while(runSimulation && !userQuit) { opcode = fetch(cpu); decode_control_signals(opcode, control); // second part of decode and write back also runSimulation = execute(cpu, control); } if(userQuit) { if(prompt("would you like to save your system state (y/n)?: ") == "y") { string memFileName = prompt("mem file name: "); string cpuFileName = prompt("cpu file name: "); saveState(memFileName.c_str(), ram, RAM_SIZE); saveState(cpuFileName.c_str(), cpu, sizeof(struct CPU)); } } } int main(int argc, char *argv[]) { ifstream infile; struct CPU *cpu; if(argc < 2) { cerr << "Usage: " << argv[0] << " memory_file [cpu_file]" << endl; return -1; } signal(SIGINT, ctrlC); // initialize our state ram = new uint8_t[RAM_SIZE]; cpu = new struct CPU; for(int idx = 0; idx < NUM_REGS; idx++) { cpu->regs[idx] = 0; } cpu->pc = 0; // setup the stack pointer cpu->regs[14] = RAM_SIZE-4-120; // setup the fib program cpu->regs[8] = RAM_SIZE-4; // fetch our memory image and cpu state (if set) fillState(argv[1], ram, RAM_SIZE); if(argc >= 3) { fillState(argv[2], cpu, sizeof(struct CPU)); } startTime = time(NULL); cpu_exec(cpu); return 0; }