From cfbfc26c508452dd7fe988b21f7d2e086fa978d2 Mon Sep 17 00:00:00 2001 From: "Z. J. Shi" Date: Thu, 2 Nov 2017 08:08:04 -0400 Subject: [PATCH] first commit --- README.md | 1 + hardware.py | 228 +++++++++++++++++++++++++++++++++++++++++++++++++++ mips_sim.py | 43 ++++++++++ signals.py | 48 +++++++++++ utilities.py | 146 +++++++++++++++++++++++++++++++++ 5 files changed, 466 insertions(+) create mode 100644 README.md create mode 100644 hardware.py create mode 100644 mips_sim.py create mode 100644 signals.py create mode 100644 utilities.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..ad49361 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# mips_sim_template_python diff --git a/hardware.py b/hardware.py new file mode 100644 index 0000000..36b055e --- /dev/null +++ b/hardware.py @@ -0,0 +1,228 @@ +import re +import random +from utilities import print_register, print_register_dump, print_memory_word, int_to_signed_32 + +# Hardware modules + +class Register: + """ + set_data: Set input of the register + set_write: Set the write signal to 1 + read: Read value from the register + clock: Set input of the register to the value of the register + ------ + Note that without .set_write(), .clock() will not save the input value + """ + def __init__(self): + self.data_in = 0 + self.write = 0 + self.data = 0 + def set_data(self, d): + self.data_in = d + def set_write(self, w = 1): + self.write = w + def read(self): + return self.data + def clock(self): + if (self.write != 0): + self.data = self.data_in + +class RegisterFile: + def __init__(self): + self.data = [0]*32 + self.read_register_1 = 0 + self.read_register_2 = 0 + self.write_register = 0 + self.write_data = 0 + self.read_data_1 = 0 + self.read_data_2 = 0 + self.regwrite = 0 + self.verbose = 0 + def set_read_registers(self, r1, r2): + self.read_register_1 = r1 & 0x1F + self.read_register_2 = r2 & 0x1F + def set_write_register(self, wr): + self.write_register = wr & 0x1F + def set_write_data(self, d): + self.write_data = d + def set_regwrite(self, d = 1): + self.regwrite = d + def get_read_data_1(self): + if (self.verbose > 0): + print_register ("RF_R1", self.read_register_1, self.data[self.read_register_1]) + return self.data[self.read_register_1] + def get_read_data_2(self): + if (self.verbose > 0): + print_register ("RF_R2", self.read_register_2, self.data[self.read_register_2]) + return self.data[self.read_register_2] + def clock(self): + if (self.regwrite != 0): + if (self.write_register > 0): + self.data[self.write_register] = self.write_data + if (self.verbose > 0): + print_register("RF_W", self.write_register, self.write_data) + def set_verbose(self, v): + self.verbose = v + def dump(self): + for i in range(len(self.data)): + print_register_dump(i, self.data[i]) + if ((i & 3) == 3): + print(""); + +class Memory: + """ + set_address: Set the address of I/O operation + set_data: Set input data + set_memread: Set the memory read signal to 1 + set_memwrite: Set the memory write signal to 1 + get_data: Read value from out + clock: Set input of the register to the value of the register + run: To execute read/write memory + ------ + Note that without .set_write(), .clock() will not save the input value + """ + def __init__(self): + self.data = {} + self.address = 0 + self.data_in = 0 + self.data_out = 0 + self.read = 0 + self.write = 0 + self.verbose = 0 + def set_address(self, addr): + self.address = addr + def set_data(self, d): + self.data_in = d + def set_memread(self, v = 1): + self.read = v + def set_memwrite(self, v = 1): + self.write = v + def get_data(self): + return self.data_out + def run(self): + if (self.read == 0 and self.write == 0): + return + if (self.address < 0 or self.address >= 0x80000000): + print("Error: Membery address 0x{0:08X} ({0:d}) is too large.".format(self.address)) + if ((self.address & 3) != 0): + print("Error: Membery address 0x{0:08X} ({0:d}) is not aligned.".format(self.address)) + if (self.read != 0): + if (self.address in self.data): + self.data_out = self.data[self.address] + else: + self.data_out = 0 + if (self.verbose > 0): + print_memory_word ("MEM_R", self.address, self.data_out) + elif (self.write != 0): + self.data[self.address] = self.data_in + if (self.verbose > 0): + print_memory_word ("MEM_W", self.address, self.data_in) + + def get_starting_address(self): + if (len(self.data) == 0): + return 0 + return min(self.data) + # return min(self.data.keys()) + + def get_ending_address(self): + if (len(self.data) == 0): + return 0 + return max(self.data) + # return max(self.data.keys()) + + def set_verbose(self, v): + self.verbose = v + + def dump(self): + for addr in sorted(self.data.keys()): + print_memory_word ("MEM_D", addr, self.data[addr]) + +MAXINT32 = 2147483647 +MININT32 = -2147483648 + +def ALU_32 (n1, n2, alu_control): + """ + Input 0000: And + Input 0001: Or + Input 0010: Addition + Input 0110: Substraction + Input 0111: Set Less Then + Output: + result: Calculated result of the ALU + zero: Zero signal of ALU(1 if result is zero) + """ + result = 0 + if (alu_control == 0): + result = int_to_signed_32(n1 & n2) + elif (alu_control == 1): + result = int_to_signed_32(n1 | n2) + elif (alu_control == 2): + result = (n1 + n2) + if (result > MAXINT32 or result < MININT32): + raise ValueError("Arithmetic overflow from addition %d." % result) + elif (alu_control == 6): + result = (n1 - n2) + if (result > MAXINT32 or result < MININT32): + raise ValueError("Arithmetic overflow from subtraction %d." % result) + elif (alu_control == 7): + result = 1 if (n1 < n2) else 0 + """ + n1 &= 0xFFFFFFFF + n2 &= 0xFFFFFFFF + sign1 = (n1 >> 31) & 1 + sign2 = (n2 >> 31) & 1 + if (sign1 != sign2): + result = 1 if (sign1 > sign2) else 0 + else: + result = 1 if (n1 < n2) else 0 + """ + # can check overflow here + # result >> 31 should be 0 or -1 + zero = 1 if (result == 0) else 0 + return (result, zero) + +def MUX_2_1(o_0, o_1, control): + """ + 2-1 MUX + """ + return o_1 if control else o_0 + +def AND_2(d0, d1): + """ + Two-input AND gate. + """ + return (d0 & d1) + + +if __name__ == '__main__': + test = 'MEM' + print ("Test " + test) + if (test == 'RF'): + RF = RegisterFile() + RF.set_verbose(1) + RF.set_regwrite(1) + for i in range(32): + RF.set_write_register(i) + v = (int((random.random() - 0.5) * 0x100000000)) + RF.set_write_data(v) + RF.clock() + RF.dump() + elif (test == 'MEM'): + MEM = Memory() + MEM.set_verbose(1) + MEM.dump() + MEM.set_memwrite(1) + MEM.set_memread(0) + + a = 0x40240; + for i in range(16): + MEM.set_address(a) + v = (int((random.random() - 0.5) * 0x100000000)) + MEM.set_data(v) + MEM.run() + a += 4 + MEM.dump() + from utilities import println_int + println_int ("Staring address", MEM.get_starting_address()) + println_int ("Ending address", MEM.get_ending_address()) + diff --git a/mips_sim.py b/mips_sim.py new file mode 100644 index 0000000..0c45b32 --- /dev/null +++ b/mips_sim.py @@ -0,0 +1,43 @@ +import sys, re +import core_sc, utilities + +def sys_error(s): + print ("Error: " + s) + exit(1) + +argc = len(sys.argv) +if (argc < 2 or argc > 4): + sys_error("Usage: mips_sim input_file [num_cycles] [-v]") + +verbose = 0 +cycles = 0 +mode = 0 + +for a in sys.argv[2:]: + if (a == '-v'): + verbose = 1 + elif (a == '-p1'): + mode = 1 + elif (a == '-q'): + mode = 0xE + else: + cycles = int(a) + +if (cycles < 0): + sys_error("Number of cycles must be nonnegative ({}).".format(cycles)); + +core = core_sc.Core_SC() +utilities.load_file(core.I_Mem, sys.argv[1]) +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) + +actual_cycles = core.run(cycles) + +core.RF.dump() +core.D_Mem.dump() +print("Number of cycles=%d" % actual_cycles) + diff --git a/signals.py b/signals.py new file mode 100644 index 0000000..d8aa1fe --- /dev/null +++ b/signals.py @@ -0,0 +1,48 @@ + +class Signals: + def __init__(self): + self.reset() + + def reset(self): + # already generated + self.PC = self.PC_4 = self.instruction = 0 + + # signals that are extracted from instruction + self.opcode = self.funct = self.rs = self.rt = self.rd = self.immediate = 0 + + # generated by the main control from opcode + self.RegDst = self.Jump = self.Branch = self.MemRead = self.MemtoReg = self.ALUOp = self.MemWrite = self.ALUSrc = self.RegWrite = 0 + + # generated by sign extend + self.Sign_extended_immediate = 0 + + # generated by ALU control + self.ALU_operation = 0 + + # calculated address + self.Branch_address = self.Jump_address = 0 + + # Write_register can be decided early + self.Write_register = self.Write_data = 0 + + # Register file read data + # read_register_1 = rs read_register_2 = rt + self.RF_read_data_1 = self.RF_read_data_2 = 0 + + # ALU + # ALU_input_1 = RF_read_data_1 + self.ALU_input_2 = self.ALU_result = self.Zero = 0 + # for software only. You need to move value to ALU_result and ALU_Zero. + self.ALU_returned_value = [0, 0] + + # D_Mem + # D_Mem address = ALU_result + self.MEM_read_data = 0 + + # PC_branch is the output of the MUX controlled by branch + self.PCSrc = self.PC_branch = 0 + + # PC that will be used to fetch instruction in the next cycle + self.PC_new = 0 + + # Add more signals here diff --git a/utilities.py b/utilities.py new file mode 100644 index 0000000..b0a9266 --- /dev/null +++ b/utilities.py @@ -0,0 +1,146 @@ + +def print_new_cycle(cycle_num): + print("==========================================\nCycle {:d}".format(cycle_num)) + +def print_memory_word (s, addr, i): + ui = i & 0xFFFFFFFF + print("{0} 0x{1:08X} 0x{2:08X} {3:d}".format(s, addr, ui, i)) + +def print_register (s, num, i): + ui = i & 0xFFFFFFFF + print("{0} ${1:02d}=0x{2:08X} {3:d}".format(s, num, ui, i)) + +def print_register_dump (num, i): + ui = i & 0xFFFFFFFF + print("${0:02d}=0x{1:08X}({2:11d}) ".format(num, ui, i), end='') + +def print_int(s, i): + print("{0}=0x{1:08X} {2:d}".format(s, i & 0xFFFFFFFF, i), end='') + +def println_int(s, i): + print("{0}=0x{1:08X} {2:d}".format(s, i & 0xFFFFFFFF, i)) + +def int_to_signed_32 (v): + """ + Input: v is an integer + Output: the value if the lower 32 bits are considered as 2's complement numbers + """ + v = v & 0xFFFFFFFF # only lower 32 bits + if (v & 0x80000000) : # check the sign + v = v - 0x100000000 + return v + +def int_to_unsigned_32 (v): + """ + Input: v is an integer + Output: Only lower 32 bits are kept + """ + return (v & 0xFFFFFFFF) # only lower 32 bits + +def int_to_signed_16 (v): + """ + Input: v is an integer + Output: the value if the lower 16 bits are considered as 2's complement numbers + """ + v = v & 0xFFFF # only lower 16 bits + if (v & 0x8000) : # check the sign + v = v - 0x10000 + return v + +def print_signal(name, v, n): + """ + Input: Name, v, n + Output: None + """ + if (n == 1) : + v = v & 1 + fmt = "" + print (name+"="+str(v)) + elif (n < 16) : + fmt = "0b{0:0"+str(n)+"b}"; + elif (n == 16) : + fmt = "0x{0:04X}"; + v = v & 0xFFFF; + else : + fmt = "0x{0:08X}"; + v = v & 0xFFFFFFFF; + if (fmt != "") : + print (name+"="+fmt.format(v)+" "+str(int_to_signed_32(v))) + return + +def print_signals_1(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); + +def print_signals_2(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); + +def load_file(MEM, filename): + # load data/instructions from the file + import re + pattern = re.compile('^((0x)*([0-9A-Fa-f]+))\s+((0x)*([0-9A-Fa-f]+))') + MEM.set_memwrite(1); + MEM.set_memread(0); + with open(filename) as file: + for line in file: + m = re.search(pattern, line) + if (m): + address = int(m.group(1), 0) + value = int(m.group(4), 0) + MEM.set_address(address); + MEM.set_data(value); # may use int_to_signed_32(value) + MEM.run(); + # print ("Mem[%s]=%s"% ( hex(address),hex(value))) + #need to set the value in memory module + MEM.set_memwrite(0); + return + +if __name__ == '__main__': + test = 'sig' + "Test " + test + if (test == 'print'): + print_signal("B", 1, 1) + print_signal("R", 31, 5) + print_signal("Immd", 0xFABC, 16) + print_signal("PC", 0x040000C4, 32) + print_signal("RV", -1, 32) + print_int("NoNL", -1) + println_int("WithNL", -2) + elif (test == 'load'): + from hardware import Memory + I_Mem = Memory() + load_file(I_Mem, "input.txt") + I_Mem.dump() + elif (test == 'sig'): + from signals import Signals + sig = Signals() + print_signals_1(sig) + print_signals_2(sig) +