diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..0cbf9cd --- /dev/null +++ b/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..accea29 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + mips_sim_java_template + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..191641d --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# mips_sim_template_java + diff --git a/src/CombModule.java b/src/CombModule.java new file mode 100644 index 0000000..0350deb --- /dev/null +++ b/src/CombModule.java @@ -0,0 +1,39 @@ +public final class CombModule { + public static int[] ALU_32(int n1, int n2, int alu_control) + { + int result = 0; + int zero; + if (alu_control == 0) // and + result = (n1 & n2); + else if (alu_control == 1) // or + result = (n1 | n2); + else if (alu_control == 2) // add + result = n1 + n2; + else if (alu_control == 6) // sub + result = n1 - n2; + else if (alu_control == 7) // slt + { + if (n1 < n2) + result = 1; + else + result = 0; + } + // result &= 0xFFFFFFFF; // no need for Java + if (result == 0) + zero = 1; + else + zero = 0; + return new int[] {result, zero}; + } + + public static int MUX_2_1(int d0, int d1, int sel) + { + return ((sel == 0) ? d0 : d1); + } + + public static int AND_2(int d0, int d1) + { + return (d0 & d1); + } + +} \ No newline at end of file diff --git a/src/Core_SC.java b/src/Core_SC.java new file mode 100644 index 0000000..7fb5c2c --- /dev/null +++ b/src/Core_SC.java @@ -0,0 +1,192 @@ + +public class Core_SC { + + private static final int OPCODE_R = 0; + private static final int OPCODE_J = 2; + private static final int OPCODE_BEQ = 4; + private static final int OPCODE_SW = 0x2B; + private static final int OPCODE_LW = 0x23; + private static final int OPCODE_ADDI = 8; + + + public Memory I_Mem; + public Memory D_Mem; + public RegisterFile RF; + + private Register RegPC; + private Signals signals; + + private int cycle_num; + private int mode; + + // Functions + public Core_SC() + { + RF = new RegisterFile(); + RegPC = new Register(); + I_Mem = new Memory(); + D_Mem = new Memory(); + signals = new Signals(); + + cycle_num = 0; + } + + public void set_PC(int pc) + { + RegPC.set_data(pc); + RegPC.set_write(1); + } + + public void set_mode(int m) + { + mode = m; + } + + // Your Code + public int run(int n_cycles) + { + int i_cycles = 0; + int ending_PC; + + I_Mem.set_memread(1); + I_Mem.set_memwrite(0); + ending_PC = I_Mem.get_ending_address(); + + while (n_cycles == 0 || i_cycles < n_cycles) + { + if ((mode & 2) == 0) Utilities.print_new_cycle(cycle_num); + i_cycles ++; + cycle_num ++; + + // clock changes + RegPC.clock(); + RF.clock(); + + // read PC + signals.PC = RegPC.read(); + signals.PC_4 = signals.PC_new = signals.PC + 4; // PC + 4 + + if ((mode & 2) == 0) Utilities.println_int("PC", signals.PC); + if (signals.PC > ending_PC) { + if ((mode & 2) == 0) System.out.println("No More Instructions"); + i_cycles --; + break; + } + + I_Mem.set_address(signals.PC); + I_Mem.run(); + signals.instruction = I_Mem.get_data(); + if ((mode & 2) == 0) Utilities.println_int("instruction", signals.instruction); + + // Now you have PC and the instruction + // Some signals' value can be extracted from instruction directly + signals_from_instruction(signals.instruction, signals); + + // call main_control + main_control(signals.opcode, signals); + + // call sign_extend + signals.Sign_extended_immediate = sign_extend(signals.immediate); + + // Write_register. Also an example of using MUX + signals.Write_register = CombModule.MUX_2_1(signals.rt, signals.rd, signals.RegDst); + + // ALU control + signals.ALU_operation = ALU_control(signals.ALUOp, signals.funct); + + // Calculate branch address + signals.Branch_address = calculate_branch_address(signals.PC_4, signals.Sign_extended_immediate); + signals.Jump_address = calculate_jump_address(signals.PC_4, signals.instruction); + + // Print out signals generated in Phase 1. + if ((mode & 4) == 0) Utilities.print_signals_1(signals); + + if ((mode & 1) != 0) { + RegPC.set_data(signals.PC_4); + RegPC.set_write(1); + continue; + } + + // You will continue to complete the core in phase 2 + // Use RF, ALU, D_Mem + // Preapre RF write + // Compute PC_new + + RegPC.set_data(signals.PC_new); + RegPC.set_write(1); + + // Print out signals generated in Phase 2. + if ((mode & 8) == 0) Utilities.print_signals_2(signals); + } + return i_cycles; + } + + // Generate signals that can be extracted from instruction + private void signals_from_instruction (int instruction, Signals sig) + { + /* Extract the following signals from instruction. + opcode, rs, rt, rd, funct, immediate + */ + sig.opcode = (instruction >> 26) & 0x3F; + } + + // Main control unit. Generate control signals from opcode. + // The signal values are stored in a Signals object. + private void main_control (int opcode, Signals sig) + { + // Set all signals to 0 first + sig.RegDst = 0; + sig.Jump = 0; + sig.Branch = 0; + sig.MemRead = 0; + sig.MemtoReg = 0; + sig.ALUOp = 0; + sig.MemWrite = 0; + sig.ALUSrc = 0; + sig.RegWrite = 0; + if (opcode == OPCODE_R) { + sig.RegDst = 1; + sig.ALUOp = 2; + sig.RegWrite = 1; + } + // You can throw an exception on unknown opcode. + // throw new IllegalArgumentException("Error: Invalid opcode " + opcode); + } + + /* Perform sign extension on immd. + Sign extend module. + Convert 16 bits to an int. + Extract the lower 16 bits. + If bit 15 of immd is 1, compute the correct negative value (immd - 0x10000). + */ + private int sign_extend (int immd) + { + immd = immd & 0xFFFF; + return immd; + } + + // return 4 bits (as an int) ALU_operation from ALUop and funct code. + private int ALU_control(int alu_op, int funct) + { + int alu_control_out = 0; + if (alu_op == 0) // lw,sw,addi + alu_control_out = 2; // 0010 add + // else + // throw new IllegalArgumentException("Error: Invalid ALUOp " + alu_op); + return alu_control_out; + } + + private int calculate_branch_address(int pc_4, int extended) + { + int addr = 0; + + return addr; + } + + private int calculate_jump_address(int pc_4, int instruction) + { + int addr = 0; + + return addr; + } +} diff --git a/src/MIPS_Sim.java b/src/MIPS_Sim.java new file mode 100644 index 0000000..9801489 --- /dev/null +++ b/src/MIPS_Sim.java @@ -0,0 +1,59 @@ + + +public class MIPS_Sim { + + public static void main(String[] args) { + // TODO Auto-generated method stub + + int verbose = 0; + int cycles = 0; + int mode = 0; + + if (args.length < 1 || args.length > 3) { + System.err.println("Error: Please specify an input file."); + System.exit(1); + } + + for (int i = 1; i < args.length; i ++) { + if (args[i].equals("-v")) { + verbose = 1; + } else if (args[i].equals("-p1")) { + mode = 1; + } else if (args[i].equals("-q")) { + mode = 0xE; + } else { + try { + cycles = Integer.parseInt(args[i]); + } catch (NumberFormatException e) { + System.err.println("Error: " + args[i] + " is not a valid integer."); + System.exit(1); + } + if (cycles < 0) { + System.err.println("Error: Number of cycles must be nonnegative."); + System.exit(1); + } + } + } + /* + { + String cwd = System.getProperty("user.dir"); + System.out.println("Current working directory in Java : " + cwd); + System.out.println("Input file name : " + args[0]); + } + */ + Core_SC core = new Core_SC(); + Utilities.load_file(core.I_Mem, args[0]); + core.I_Mem.dump(); + core.set_PC(core.I_Mem.get_starting_address()); + + core.I_Mem.set_verbose(verbose); + core.RF.set_verbose(verbose); + core.set_mode(mode); + + int actual_cycles = core.run(cycles); + + core.RF.dump(); + core.D_Mem.dump(); + System.out.println("Number of cycles=" + actual_cycles); + } +} diff --git a/src/Memory.java b/src/Memory.java new file mode 100644 index 0000000..61b0748 --- /dev/null +++ b/src/Memory.java @@ -0,0 +1,120 @@ +import java.util.TreeMap; + +public class Memory { + + private TreeMap data; + private int address; + private int data_in, data_out; + private int read, write; + + // The following members are not hardware signals. + private int verbose; + + public Memory() + { + data = new TreeMap(); + address = 0; + data_in = 0; + data_out = 0; + read = 0; + write = 0; + + verbose = 0; + } + + public void set_address(int addr) + { + address = addr; + } + + public void set_data(int d) + { + data_in = d; + } + + public void set_memread(int v) + { + read = v; + } + + public void set_memwrite(int v) + { + write = v; + } + + public int get_data() + { + return data_out; + } + + public void run() + { + data_out = 0; + if (read == 0 && write == 0) + return; + + if (address < 0) + System.err.format("Error: Membery address 0x%08X (%d) is too large.\n", address, address); + if ((address & 3) != 0) + System.err.format("Error: Membery address 0x%08X (%d) is not aligned.\n", address, address); + if (read == 1) + { + if (data.containsKey(address)) + data_out = data.get(address); + else + data_out = 0; + if (verbose > 0) + System.out.format("MEM_R 0x%08X 0x%08X %d\n", address, data_out, data_out); + } + else if (write == 1) + { + data.put(address, data_in); + if (verbose > 0) + System.out.format("MEM_W 0x%08X 0x%08X %d\n", address, data_in, data_in); + } + } + + public int get_starting_address() + { + if (data.isEmpty()) + return 0; + return data.firstKey(); + } + + public int get_ending_address() + { + if (data.isEmpty()) + return 0; + return data.lastKey(); + } + + public void set_verbose(int v) + { + verbose = v; + } + + public void dump() + { + if (data.isEmpty()) + return; + + Integer k = data.firstKey(); + + while (k != null) { + int v = data.get(k); + System.out.format("MEM_D 0x%08X 0x%08X %d\n", k, v, v); + k = data.higherKey(k); + } + + /* Another implementation. Need to import more modules. + Set> data_set = data.entrySet(); + Iterator> itr_data = data_set.iterator(); + + while(itr_data.hasNext()) { + Entry w = itr_data.next(); + System.out.format("D %08X %08X %d\n", w.getKey(), w.getValue(), w.getValue()); + } + */ + } + +} diff --git a/src/Register.java b/src/Register.java new file mode 100644 index 0000000..8592e34 --- /dev/null +++ b/src/Register.java @@ -0,0 +1,34 @@ +public class Register { + + private int data, data_in; + private int write; + + + public Register() + { + data_in = 0; + write = 0; + data = 0; + } + + public void set_data(int d) + { + data_in = d; + } + + public void set_write(int w) + { + write = w; + } + + public int read() + { + return data; + } + + public void clock() + { + if (write != 0) + data = data_in; + } +} diff --git a/src/RegisterFile.java b/src/RegisterFile.java new file mode 100644 index 0000000..81a3e1c --- /dev/null +++ b/src/RegisterFile.java @@ -0,0 +1,89 @@ +import java.util.Arrays; + +public class RegisterFile { + + private int[] data; + private int read_register_1, read_register_2, write_register, write_data; + private int regwrite; + + private int verbose; + + public RegisterFile() + { + data = new int[32]; + Arrays.fill(data, 0); + read_register_1 = 0; + read_register_2 = 0; + write_register = 0; + write_data = 0; + regwrite = 0; + } + + public void set_read_registers(int r1, int r2) + { + read_register_1 = r1 & 0x1F; + read_register_2 = r2 & 0x1F; + } + + public void set_write_register(int wr) + { + write_register = wr & 0x1F; + } + + public void set_write_data(int d) + { + write_data = d; + } + + public void set_regwrite(int regw) + { + regwrite = regw; + } + + public int get_read_data_1() + { + if (verbose > 0) + System.out.format("RF_R1 $%02d 0x%08X %d\n", read_register_1, data[read_register_1], data[read_register_1]); + return data[read_register_1]; + } + + public int get_read_data_2() + { + if (verbose > 0) + System.out.format("RF_R2 $%02d 0x%08X %d\n", read_register_2, data[read_register_2], data[read_register_2]); + return data[read_register_2]; + } + + public void clock() + { + if (regwrite != 0) + { + if (write_register > 0) + { + data[write_register] = write_data; + if (verbose > 0) + System.out.format("RF_W $%02d 0x%08X %d\n", write_register, write_data, write_data); + } + } + } + + public void set_verbose(int v) + { + verbose = v; + } + + public void dump() + { + int i, nreg; + + nreg = data.length; + for (i = 0; i < nreg; i ++) + { + System.out.format("$%02d=0x%08X(%11d) ", i, data[i], data[i]); + + if ((i & 3) == 3) + System.out.println(""); + } + } + +} diff --git a/src/Signals.java b/src/Signals.java new file mode 100644 index 0000000..0222f59 --- /dev/null +++ b/src/Signals.java @@ -0,0 +1,48 @@ + +public final class Signals { + + // already generated + public int PC, PC_4, instruction; + + // signals that are extracted from instruction + public int opcode, funct, rs, rt, rd, immediate; + + // generated by the main control from opcode + public int RegDst, Jump, Branch, MemRead, MemtoReg, ALUOp, MemWrite, ALUSrc, RegWrite; + + // generated by sign extend + public int Sign_extended_immediate; // sign extended immediate + + // generated by ALU control + public int ALU_operation; + + // calculated address + public int Branch_address, Jump_address; + + // Write_register can be decided early + public int Write_register, Write_data; + + // Register file read data + // read_register_1 = rs read_register_2 = rt + public int RF_read_data_1, RF_read_data_2; + + // ALU + // ALU_input_1 = RF_read_data_1 + public int ALU_input_2, ALU_result, Zero; + // for software only. You need to move value to ALU_result and ALU_Zero. + public int[] ALU_returned_value; + + + // D_Mem + // D_Mem address = ALU_result + public int MEM_read_data; + + // PC_branch is the output of the MUX controlled by branch + public int PCSrc, PC_branch; + + // PC that will be used to fetch instruction in the next cycle + public int PC_new; + + // Add more signals here + +} diff --git a/src/Utilities.java b/src/Utilities.java new file mode 100644 index 0000000..1460499 --- /dev/null +++ b/src/Utilities.java @@ -0,0 +1,125 @@ +import java.io.*; +import java.util.*; +import java.util.regex.*; + +public class Utilities { + + public static int hexstr_to_int(String hex_str) { + long v = Long.parseLong(hex_str, 16); + return (int)v; + } + + public static int str_to_int(String str) { + long v = Long.decode(str); + return (int)v; + } + + // nbits must be less than 32 + public static String int_to_bin_string(int v, int nbits) + { + v = (v & ((1 << nbits) - 1)) | (1 << nbits); + return Integer.toBinaryString(v).substring(1); + } + + public static void print_int(String s, int i) + { + System.out.format("%s=0x%08X %11d", s, i, i); + } + + public static void println_int(String s, int i) + { + System.out.format("%s=0x%08X %11d\n", s, i, i); + } + + public static void print_signal(String s, int v, int n) + { + if (n == 1) { + System.out.format("%s=%d\n", s, v & 1); + } + else if (n < 16) { + System.out.format("%s=0b%s %d\n", s, int_to_bin_string(v, n), v); + } + else if (n == 16) { + v = v & 0xFFFF; + System.out.format("%s=0x%04X %d\n", s, v, v); + } + else { + System.out.format("%s=0x%08X %d\n", s, v, v); + } + } + + public static void print_new_cycle(int cycle_num) + { + System.out.println("==========================================\nCycle " + cycle_num); + } + + public static void print_signals_1(Signals sig) + { + print_signal("opcode", sig.opcode, 6); + print_signal("funct", sig.funct, 6); + print_signal("rs", sig.rs, 5); + print_signal("rt", sig.rt, 5); + print_signal("rd", sig.rd, 5); + print_signal("immediate", sig.immediate, 16); + print_signal("RegDst", sig.RegDst, 1); + print_signal("Jump", sig.Jump, 1); + print_signal("Branch", sig.Branch, 1); + print_signal("MemRead", sig.MemRead, 1); + print_signal("MemtoReg", sig.MemtoReg, 1); + print_signal("ALUOp", sig.ALUOp, 1); + print_signal("MemWrite", sig.MemWrite, 1); + print_signal("ALUSrc", sig.ALUSrc, 1); + print_signal("RegWrite", sig.RegWrite, 1); + print_signal("Sign_extended_immediate", sig.Sign_extended_immediate, 32); + print_signal("ALU_operation", sig.ALU_operation, 4); + print_signal("Branch_address", sig.Branch_address, 32); + print_signal("Jump_address", sig.Jump_address, 32); + print_signal("Write_register", sig.Write_register, 5); + } + + public static void print_signals_2(Signals sig) + { + print_signal("RF_read_data_1", sig.RF_read_data_1, 32); + print_signal("RF_read_data_2", sig.RF_read_data_2, 32); + print_signal("ALU_input_2", sig.ALU_input_2, 32); + print_signal("ALU_result", sig.ALU_result, 32); + print_signal("Zero", sig.Zero, 1); + print_signal("MEM_read_data", sig.MEM_read_data, 32); + print_signal("Write_data", sig.Write_data, 32); + print_signal("PCSrc", sig.PCSrc, 1); + print_signal("PC_branch", sig.PC_branch, 32); + print_signal("PC_new", sig.PC_new, 32); + } + + public static void load_file(Memory IMem, String filename) + { + String line; + BufferedReader br; + String pattern = "^(0x\\p{XDigit}+)\\s+(0x\\p{XDigit}+)"; + Pattern r = Pattern.compile(pattern); + int addr; + long data; + + IMem.set_memread(0); + IMem.set_memwrite(1); + try { + br = new BufferedReader(new FileReader(filename)); + + while ((line = br.readLine()) != null) { // while loop begins here + // System.out.println(line); + Matcher m = r.matcher(line); + if (m.find()) { + // System.out.println(m.group(1) + " " + m.group(2)); + addr = Integer.decode(m.group(1)); + data = Long.decode(m.group(2)); + IMem.set_address(addr); + IMem.set_data((int)data); // only the lower 32 bits + IMem.run(); + } + } // end while + } catch (IOException e){ + System.err.println("Error: " + e.getMessage()); + } + IMem.set_memwrite(0); + } +}