diff options
Diffstat (limited to 'MicSim')
| -rw-r--r-- | MicSim/components/__init__.py | 0 | ||||
| -rw-r--r-- | MicSim/components/caretaker.py | 34 | ||||
| -rw-r--r-- | MicSim/components/ijvm.py | 30 | ||||
| -rw-r--r-- | MicSim/components/microprogram.py | 204 | ||||
| -rw-r--r-- | MicSim/components/ram.py | 83 | ||||
| -rwxr-xr-x | MicSim/micsim.py | 18 | ||||
| -rw-r--r-- | MicSim/ram.txt | 2 | ||||
| -rw-r--r-- | MicSim/test/__init__.py | 0 | ||||
| -rw-r--r-- | MicSim/test/test_ram.py | 34 |
9 files changed, 405 insertions, 0 deletions
diff --git a/MicSim/components/__init__.py b/MicSim/components/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/MicSim/components/__init__.py diff --git a/MicSim/components/caretaker.py b/MicSim/components/caretaker.py new file mode 100644 index 0000000..ab20fba --- /dev/null +++ b/MicSim/components/caretaker.py @@ -0,0 +1,34 @@ +#!/usr/bin/python + +class Caretaker: + + def __init__(self): + self.objects=dict() # Create empty objects pool + # Add registers to pool + for reg in ["MAR","MDR", "PC", "MBR", "SP","LV","CPP","TOS","OPC","H"]: + self.objects[reg]=0 + self.objects["RAM"]=None + + def __getitem__(self,key): + if key=="MBRU": # If we ask for unsigned + return(abs(self.objects["MBR"])) + elif key== "MBR": + if (self.objects[key]>>7)==1: # If it a negative number (2 complement) + return(-((self.objects[key]-1)^0xFF)) # transforme bin negative number to python negative number + else: + return(self.objects[key]) + return(self.objects[key]) + + def __setitem__(self,key,value): + if key!="RAM": + if value > (2**32) and key!="MBR" and key!="MBRU": # Check value fit in word + print("Warning word overflow: value {} on register {}".format(value,key)) + value=value%(2**32) # Force to fit in word + elif value > (2**8) and key=="MBR" and key=="MBRU": # Check value fit in byte + print("Warning byte overflow: value {} on register {}".format(value,key)) + value=value%256 # Force to fit in byte + self.objects[key]=value + + def items(self): + return(self.objects.items()) + diff --git a/MicSim/components/ijvm.py b/MicSim/components/ijvm.py new file mode 100644 index 0000000..86f5f63 --- /dev/null +++ b/MicSim/components/ijvm.py @@ -0,0 +1,30 @@ + +# Quick link about opcode: https://users-cs.au.dk/bouvin/dComArk/2015/noter/Note_2/#Instructions + +# Build IJVM +ijvm=dict({ + "BIPUSH":0x10, + "DUP":0x59, + "GOTO":0xA7, + "IADD":0x60, + "IAND":0x7E, + "IFEQ":0x99, + "IFLT":0x9B, + "IF_ICMPEQ":0x9F, + "IINC":0x84, + "ILOAD":0x15, + "INVOKEVIRTUAL":0xB6, + "IOR":0x80, + "IRETURN":0xAC, + "ISTORE":0x36, + "ISUB":0x64, + "LDC_W":0x13, + "NOP":0x00, + "POP":0x57, + "SWAP":0x5F, + "WIDE":0xC4 +}) + +# Add extras instructions +ijvm["OUT"]=0x23 # Print next byte as char +ijvm["HALT"]=0x2F # Stop simulator diff --git a/MicSim/components/microprogram.py b/MicSim/components/microprogram.py new file mode 100644 index 0000000..ec84886 --- /dev/null +++ b/MicSim/components/microprogram.py @@ -0,0 +1,204 @@ + +from components.ijvm import ijvm + +class Microprogram: + + def __init__(self,components): + self.c=components # Link components to microprogram + if self.c["RAM"]==None: # Check if RAM is initialize + raise RuntimeError("Microprogram initialization fail, RAM is not initialized") + + def run(self): + """ + Start microprogram + """ + self.c["LV"]=(1024)# Place stack to 1024 + self.c["SP"]=(1024-1) # Init SP to LV-1 (because otherwise first element of the stack will be enty because of BIPUSH impl + + for i in range(1,30): # Launche first 30 insctructions + self.fetch() # Fetch + self.c["PC"]+=1 # INC PC after fetch + if self.exec()==1: # Execute opcode and halt if return code is 1 + break; + + def fetch(self): + """ + Fetch next byte from memory into MBR + """ + opcode=self.c["RAM"].fetch() + self.c["MBR"]=opcode # Opcode to MBR + + def rd(self): + """ + Read data into memory + """ + little_endian=self.c["RAM"].read() + ##### Build little endian version of MDR #### + big_endian=(little_endian&0xFF)<<24 + big_endian=big_endian|(((little_endian>>8)&0xFF)<<16) + big_endian=big_endian|(((little_endian>>16)&0xFF)<<8) + big_endian=big_endian|((little_endian>>24)&0xFF) + ############################################## + self.c["MDR"]=big_endian + + def wr(self): + """ + Write data into memory + """ + ##### Build little endian version of MDR #### + little_endian=(self.c["MDR"]&0xFF)<<24 + little_endian=little_endian|(((self.c["MDR"]>>8)&0xFF)<<16) + little_endian=little_endian|(((self.c["MDR"]>>16)&0xFF)<<8) + little_endian=little_endian|((self.c["MDR"]>>24)&0xFF) + ############################################## + big_endian=self.c["MDR"] # Backup MDR before change it to little endian + self.c["MDR"]=little_endian # Load little endian value + self.c["RAM"].write() # Write little endian value into memory + self.c["MDR"]=big_endian # Restore big endian + + def exec(self): # TODO: Implement opcode + """ + Execute next opcode + """ + opcode=self.c["MBRU"] # Get loaded OpCode (/!\ We used MBRU not MBR because MBR is signed) + if opcode==ijvm["NOP"]: # NOP + pass + elif opcode==ijvm["BIPUSH"]: # BIPUSH + self.fetch();self.c["PC"]+=1 # Fetch byte to push in MBR + self.c["SP"]+=1 # Increment stack pointer + self.c["MAR"]=self.c["SP"] # Copy SP to MAR + self.c["MDR"]=self.c["MBR"] # Set MDR to MBR + self.c["TOS"]=self.c["MBR"] # Set MDR to MBR + self.wr() # Write data to stack + elif opcode==ijvm["IADD"]: + self.c["SP"]-=1 + self.c["MAR"]=self.c["SP"] + self.c["H"]=self.c["TOS"] + self.rd() + self.c["TOS"]=self.c["MDR"]+self.c["H"] + self.c["MDR"]=self.c["TOS"] + self.wr() + elif opcode==ijvm["ISUB"]: + self.c["SP"]-=1 + self.c["MAR"]=self.c["SP"] + self.c["H"]=self.c["TOS"] + self.rd() + self.c["TOS"]=self.c["MDR"]-self.c["H"] + self.c["MDR"]=self.c["TOS"] + self.wr() + elif opcode==ijvm["POP"]: + self.c["SP"]-=1 + self.c["MAR"]=self.c["SP"] + self.rd() + self.c["TOS"]=self.c["MDR"] + elif opcode==ijvm["DUP"]: + self.c["SP"]+=1 + self.c["MAR"]=self.c["SP"] + self.c["MDR"]=self.c["TOS"] + self.wr() + elif opcode==ijvm["IAND"]: + self.c["SP"]-=1 + self.c["MAR"]=self.c["SP"] + self.c["H"]=self.c["TOS"] + self.rd() + self.c["TOS"]=(self.c["MDR"] & self.c["H"]) + self.c["MDR"]=self.c["TOS"] + self.wr() + elif opcode==ijvm["IOR"]: + self.c["SP"]-=1 + self.c["MAR"]=self.c["SP"] + self.c["H"]=self.c["TOS"] + self.rd() + self.c["TOS"]=(self.c["MDR"] | self.c["H"]) + self.c["MDR"]=self.c["TOS"] + self.wr() + elif opcode==ijvm["SWAP"]: + self.c["MAR"]=self.c["SP"]-1 + self.rd() + self.c["MAR"]=self.c["SP"] + self.c["H"]=self.c["MDR"] + self.wr() + self.c["MDR"]=self.c["TOS"] + self.c["MAR"]=self.c["SP"]-1 + self.wr() + self.c["TOS"]=self.c["H"] + elif opcode==ijvm["ILOAD"]: + self.fetch();self.c["PC"]+=1 # Fetch local variable to push onto the stack + self.c["H"]=self.c["LV"] + self.c["MAR"]=self.c["MBRU"]+self.c["H"] + self.rd() + self.c["SP"]+=1 + self.c["MAR"]=self.c["SP"] + self.wr() + self.c["TOS"]=self.c["MDR"] + elif opcode==ijvm["ISTORE"]: + self.fetch();self.c["PC"]+=1 # Fetch local variable offset where to store + self.c["H"]=self.c["LV"] + self.c["MAR"]=self.c["MBRU"]+self.c["H"] + self.c["MDR"]=self.c["TOS"] + self.wr() + self.c["SP"]-=1 + self.c["MAR"]=self.c["SP"] + self.rd() + self.c["TOS"]=self.c["MDR"] + elif opcode==ijvm["IINC"]: + self.fetch();self.c["PC"]+=1 # Fetch local variable offset to inc + self.c["H"]=self.c["LV"] + self.c["MAR"]=self.c["MBRU"]+self.c["H"] + self.rd() + self.fetch();self.c["PC"]+=1 # Fetch inc value + self.c["H"]=self.c["MDR"] + self.c["MDR"]=self.c["MBR"]+self.c["H"] + self.wr() + elif opcode==ijvm["GOTO"]: + self.fetch();self.c["PC"]+=1 # Fetch first byte + self.c["OPC"]=self.c["PC"]-1 + self.c["H"]=self.c["MBR"]<<8 + self.fetch();self.c["PC"]+=1 # Fetch second byte + self.c["H"]=self.c["MBRU"]|self.c["H"] + self.c["PC"]=self.c["OPC"]+self.c["H"] + elif opcode==ijvm["OUT"]: + self.fetch();self.c["PC"]+=1 # Fetch byte to push in MBR + print(str(chr(self.c["MBRU"])),end="") # MBRU because no char which are negative + elif opcode==ijvm["IFEQ"]: + self.c["SP"]=self.c["SP"]-1 + self.c["MAR"]=self.c["SP"] + self.c["OPC"]=self.c["TOS"] + self.rd() + self.c["TOS"]=self.c["MDR"] + if self.c["OPC"]==0: + self.T() + else: + self.F() + elif opcode==ijvm["IFLT"]: + self.c["SP"]=self.c["SP"]-1 + self.c["MAR"]=self.c["SP"] + self.c["OPC"]=self.c["TOS"] + self.rd() + self.c["TOS"]=self.c["MDR"] + if self.c["OPC"]<0: + self.T() + else: + self.F() + elif opcode==ijvm["HALT"]: + return(1) + else: + if opcode in ijvm: + print("Instruction {} not yet implemented.".format(ijvm[opcode])) + else: + raise RuntimeError("Instruction {} not found on address {}".format(opcode,self.c["PC"]-1)) + return(0) + + def T(self): # This function is here just to follow ijvm implementation of "Structured Computer Organization" + self.fetch();self.c["PC"]+=1 # exactly like GOTO implementation + self.c["OPC"]=self.c["PC"]-1 # exactly like GOTO implementation + ###### GOTO2 ##### + self.c["H"]=self.c["MBR"]<<8 + self.fetch();self.c["PC"]+=1 # Fetch second byte + self.c["H"]=self.c["MBRU"]|self.c["H"] + self.c["PC"]=self.c["OPC"]+self.c["H"] + ################## + def F(self): # This function is here just to follow ijvm implementation of "Structured Computer Organization" + self.fetch();self.c["PC"]+=1 # Needed because memory access take 1 cycle in simulation + self.c["PC"]=self.c["PC"]+1 + diff --git a/MicSim/components/ram.py b/MicSim/components/ram.py new file mode 100644 index 0000000..6a5b02e --- /dev/null +++ b/MicSim/components/ram.py @@ -0,0 +1,83 @@ +from components.ijvm import ijvm + +class Ram: + + def __init__(self,components,size): + self.data=dict() + self.lastAddr=size-1 + self.c=components + + def loadRamFile(self,filepath): + """ + Load a Ram file into self.data + """ + data=dict() + addr=0 + f=open(filepath,"r") + for line in f.readlines(): + line=line.rstrip() # remove \n + if line in ijvm: + data[addr]=int(ijvm[line]) + else: + try: + value=int(line,0) + except: + raise ValueError("Invalide RAM entry: Address {} value {}".format(addr,line)) + if value>255: + raise ValueError("Ram contain values that does not fit in a byte: value {} at address {}".format(value,addr)) + data[addr]=value + addr+=1 + f.close() + self.data=data + + def write(self): + """ + Write data to memory based Mic-1 architecture + """ + addr=self.c["MAR"]*4 # Don't forget MAR address 32bits block of memory + if addr>self.lastAddr: + raise ValueError("You get out of the ram by trying to set a value at address {}, max address is {}".format(addr,self.lastAddr)) + #### Split bytes and write #### + self.data[addr+3]=self.c["MDR"] & 0xFF + self.data[addr+2]=self.c["MDR"]>>8 & 0xFF + self.data[addr+1]=self.c["MDR"]>>16 & 0xFF + self.data[addr]=self.c["MDR"]>>24 & 0xFF + + + def read(self): + """ + Read data from memory based Mic-1 architecture + """ + addr=self.c["MAR"]*4 # Don't forget MAR address 32bits block of memory + value=None + try: + #### Combine bytes #### + value=self.data[addr]<<24|(self.data[addr+1]<<16)|(self.data[addr+2]<<8)|(self.data[addr+3]) + except: + if addr>self.lastAddr: + raise ValueError("You get out of the ram by trying to get value at address {}, max address is {}".format(addr,self.lastAddr)) + if(value==None): + return(0) + return(value) + + def fetch(self): + """ + Fetch next byte from memory based Mic-1 architecture + """ + addr=self.c["PC"] + value=None + try: + value=self.data[addr] + except: + if addr>self.lastAddr: + raise ValueError("You get out of the ram by trying to get value at address {}, max address is {}".format(addr,self.lastAddr)) + if(value==None): + return(0) + return(value) + + def dump(self): + """ + Fetch RAM data (usefull for unit tests) + """ + return(self.data) + diff --git a/MicSim/micsim.py b/MicSim/micsim.py new file mode 100755 index 0000000..a45311f --- /dev/null +++ b/MicSim/micsim.py @@ -0,0 +1,18 @@ +#!/usr/bin/python + +from components.microprogram import Microprogram +from components.ram import Ram +from components.caretaker import Caretaker + +c=Caretaker() # Init components +RAM=Ram(c,5000) # Init ram +RAM.loadRamFile("./ram.txt") # Load Ram from file +c["RAM"]=RAM # Add ram to components + + +mic=Microprogram(c) # Create micro program +mic.run() # Run the micro program + +mic.rd() +print(bin(c["MDR"])) +print(RAM.dump()) diff --git a/MicSim/ram.txt b/MicSim/ram.txt new file mode 100644 index 0000000..660c3c6 --- /dev/null +++ b/MicSim/ram.txt @@ -0,0 +1,2 @@ +BIPUSH +9 diff --git a/MicSim/test/__init__.py b/MicSim/test/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/MicSim/test/__init__.py diff --git a/MicSim/test/test_ram.py b/MicSim/test/test_ram.py new file mode 100644 index 0000000..1570e0d --- /dev/null +++ b/MicSim/test/test_ram.py @@ -0,0 +1,34 @@ + +from components.ram import Ram +import unittest +from random import randint + + +class RamTest(unittest.TestCase): + + def setUp(self): + """ + Init test + """ + self.caretaker=dict({"MDR":0,"MAR":0,"MBR":0,"PC":0}) + + def test_write(self): + """ + Test write method + """ + toWrite=randint(0,2**32) # Pick a random number to write + self.caretaker["MDR"]=toWrite + ram=Ram(self.caretaker,10000) + ram.write() # Write a random number at address 0 + + data=ram.dump() # Dump ram + ##### Test if everything is written using big endian model ##### + self.assertEqual((toWrite>>24)&0xFF,data[self.caretaker["MAR"]]) + self.assertEqual((toWrite>>16)&0xFF,data[self.caretaker["MAR"]+1]) + self.assertEqual((toWrite>>8)&0xFF,data[self.caretaker["MAR"]+2]) + self.assertEqual(toWrite&0xFF,data[self.caretaker["MAR"]+3]) + + + +if __name__ == "__main__": + unittest.main() |
