aboutsummaryrefslogtreecommitdiff
path: root/MicSim
diff options
context:
space:
mode:
Diffstat (limited to 'MicSim')
-rw-r--r--MicSim/components/__init__.py0
-rw-r--r--MicSim/components/caretaker.py34
-rw-r--r--MicSim/components/ijvm.py30
-rw-r--r--MicSim/components/microprogram.py204
-rw-r--r--MicSim/components/ram.py83
-rwxr-xr-xMicSim/micsim.py18
-rw-r--r--MicSim/ram.txt2
-rw-r--r--MicSim/test/__init__.py0
-rw-r--r--MicSim/test/test_ram.py34
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()