Beispiel #1
0
class Cache:
    """ This class represents a cache component. """
    def __init__(self, cache_size, block_size, debug):
        """ Initialize the cache component. """
        self.debug_info = debug

        if self.debug_info is True:
            print("[Cache] initializing...")

        ### Initialization code ###
        self.cache_size = cache_size
        self.block_size = block_size
        self.num_of_blocks_used = 0
        self.max_number_of_blocks = int(self.cache_size / self.block_size)
        self.cache = [Block()] * int(self.cache_size / self.block_size)
        self.bus = Bus(self.debug_info)
        ###########################

        #### Performance Stats ####
        self.hits = 0
        self.misses = 0
        self.replacements = 0
        ###########################

        if self.debug_info is True:
            print("[Cache] finished initializing...")
            print("[Cache] Max number of blocks '", self.max_number_of_blocks,
                  "'...")

    # return true if full, false otherwise
    def is_full(self):
        """ Return true if cache is full, false otherwise. """
        return self.num_of_blocks_used == self.max_number_of_blocks

    # LRU , return least recently used block's position
    def LRU(self):
        """ Return the oldest, least recently used block. """
        if self.debug_info is True:
            print("[Cache] Looking for least recently used block...")

        oldest_time = 0
        oldest_block_number = None

        for index, block in enumerate(self.cache):
            if block.timer >= oldest_time:
                oldest_time = block.timer
                oldest_block_number = index

        if self.debug_info is True:
            print("[Cache] LRU block found...")
            print("[...]      block data : ",
                  self.cache[oldest_block_number].data)
            print("[...]      block timer: ",
                  self.cache[oldest_block_number].timer)

        return oldest_block_number

    # Least Recently Used algorithms
    # insert, insert data into a block in position of previous LRU
    def insert(self, data):
        """ Insert a new block in the LRU block's position. """
        if self.debug_info is True:
            print("\n[Cache] inserting block...")
            print("[...]        Data: ", data)
            print("[...] Current Cache table: ")
            self.print_cache()

        if self.is_full() is True:
            # Swap blocks if full
            print("[...] Cache full...")
            lru_block = self.LRU()

            if self.debug_info is True:
                print("[Cache] LRU block to be replaced: ")
                print("[...]    data: ", self.cache[lru_block].data)
                print("[...]   timer: ", self.cache[lru_block].timer)

            self.cache[lru_block] = Block()
            self.cache[lru_block].valid = 1
            self.cache[lru_block].data = data

            self.print_cache()
        else:
            # Otherwise, place into an open spot
            print("[...] Cache has empty slots...")
            position = self.find_empty_spot()
            self.cache[position] = Block()
            self.cache[position].data = data
            self.cache[position].valid = 1
            self.num_of_blocks_used = self.num_of_blocks_used + 1

    def find_empty_spot(self):
        """ Find an empty spot in cache. Return index. """
        for index, block in enumerate(self.cache):
            if block.valid == 0:
                return index
        return 0

    def find_block(self, address):
        """ Returns "HIT" or "MISS" depending on if data is present. """
        if self.debug_info is True:
            print("[Cache] looking for block with address '" + address +
                  "'...")

        for block in self.cache:
            if block.data[0] == address:
                return block.data[1]

        # If this point reached, return Miss
        return "MISS"

    def look_for_block_addr_in_tlb(self, address, callee, callee_name):
        """ If data was missing, and find and store. """
        if self.debug_info is True:
            print("[Cache] Looking for new block in TLB...")

        block_address = self.bus.communicate(
            "cache", callee, callee_name, "TLB, physical address of virtual",
            address)
        return block_address

    def retrieve_block(self, address, callee, callee_name):
        """ Use bus to get block with specified address. Return block. """
        if self.debug_info is True:
            print("[Cache] Retrieving missing block...")
        block = self.bus.communicate("cache", callee, callee_name,
                                     "memory, give block", address)
        self.insert([address, block])

    def update_timer(self):
        """ Update the timers for all blocks in memory, increment by 1. """
        for block in self.cache:
            block.timer = block.timer + 1

    def print_stats(self):
        """ Print the hit ratio, miss ratio, and replacement ratios. """
        print("[Cache] Printing cache statistics:")
        total = self.hits + self.misses + self.replacements

        # Avoid dividing by 0
        if total == 0:
            total = 1

        print("[...]    hit ratio:         ", (self.hits / (total)))
        print("[...]    miss ratio:        ", (self.misses / (total)))
        print("[...]    replacement ratio: ", (self.replacements / (total)))

    def print_cache(self):
        """ Print the contents of the cache. """
        counter = 0
        print("[Cache] Printing contents of cache:")
        print("[...] Set | B1 timer | B1 data\t| B2 timer | B2 data\t")
        for i in range(0, len(self.cache) - 1, 2):
            block1 = self.cache[i]
            block2 = self.cache[i + 1]
            print("[...] ", counter, " | ", block1.timer, " | ", block1.data,
                  " | ", block2.timer, " | ", block2.data)
            counter = counter + 1
Beispiel #2
0
class Memory:
    """ This class handles the activity of the memory and virtual memory component. """
    def __init__(self, memory_size, virtual_memory_size, debug):
        self.debug_info = debug

        if self.debug_info is True:
            print("[Memory] initializing...")

        ### Initialization code ###
        self.virtual_memory = [None] * virtual_memory_size
        self.memory = [None] * memory_size
        self.page_table_register = None
        self.bus = Bus(debug)
        self.memory_size = memory_size
        self.number_of_pages_loaded = 0
        self.memory_used = 0

        ### Virtual Memory ###
        # Assuming size of memory is
        self.stack_address_used = 0x8000 # decrease as memory is used, stack
                                         # grows up
        self.const_stack_start = 0x8000
        self.heap_address_used = 0       # increase as memory is used
        self.const_heap_start = 0x4000
        self.bss_address_used = 0        # increase as memory is used
        self.const_bss_start = 0x2000
        self.text_address_used = 0       # increase as memory is used
        self.const_text_start = 0x1000
        ###########################

        if self.debug_info is True:
            print("[Memory] finished initializing...")

    def load_initial_pages_of_program(self, callee, callee_name):
        """ Start from 'main:' label and load all subsequent pages into memory. """
        self.page_table_register = self.bus.communicate("memory", callee, callee_name,
                                                        "disk, initial page number", "")
        page_size = self.bus.communicate("memory", callee, callee_name,
                                         "disk, send page size", "")
        while (self.memory_size - self.memory_used) >= page_size:
            page = self.bus.communicate("memory", callee, callee_name, "disk, send page",
                                        str(self.page_table_register))
            self.store_page(page)
            self.page_table_register = self.page_table_register + 1

    # Place page in open spot in memory
    def store_page(self, page):
        """ Store given page in memory. """
        if self.debug_info is True:
            print("[Memory] Attempting to store disk page ", page.page_number, "...")

        if (self.memory_size - self.memory_used) < page.page_size:
            if self.debug_info is True:
                print("[Memory] Memory (Text) full, replacing pages...")
                print("[ERROR] Not implemented yet.")
            #replace_page(page)
        else:
            # store page, instruction by instruction
            counter = self.memory_used
            for instruction in page.instructions:
                self.memory[counter] = instruction
                self.memory[counter+1] = "."    # These are placeholders used to simulate
                self.memory[counter+2] = "."    # byte addressing, ...
                self.memory[counter+3] = "."    # ...
                counter = counter + 4
                self.memory_used = self.memory_used + 4

    def replace_page(self, page):
        """ Replace random page in memory with given page. """
        if self.debug_info is True:
            print("[Memory] Replacing memory page randomly with page", page.page_number, "...")

        # memory_size/ page_size = number of page positions
        possible_page_positions = self.memory_size / page.page_size - 1
        picked_position = randint(0, possible_page_positions)

        counter = 0 + (page.page_size * picked_position)

        for instruction in page.instructions:
            self.memory[counter] = instruction
            counter = counter + 4

        # update_virtual_memory(page.page_number)

    def print_memory_page_table(self):
        """ Print the contents of memory. """
        if self.debug_info is True:
            print("\n[Memory] Printing contents of memory...")
        unallocated = "...unallocated..."
        for index, instruction in enumerate(self.memory):
            if instruction is None:
                output = unallocated
            else:
                output = instruction

            if instruction == ".":
                continue
            else:
                print("[...] ", phex(index, 10), " | ", output)

    def map_pages_to_virtual(self, callee, callee_name):
        """
            Synchronize virtual memory with the contents of memory, map memory addresses
            to virtual addresses.
        """
        if self.debug_info is True:
            print("[V. Memory / Memory] Mapping memory pages to virtual memory...")
        counter = self.const_text_start

        all_pages = self.bus.communicate("virtual memory", callee, callee_name,
                                         "disk, all pages for mapping", "")

        for page in all_pages:
            for instruction in page.instructions:
                self.virtual_memory[counter] = instruction
                counter = counter + 4

    def update_virtual_memory(self, page_number, position):
        """ Update the virtual memory addresses, with currently loaded memory pages. """
        pass

    def print_virtual_with_position(self, address):
        """ Print the position of the simulator in virtual memory with given PC address. """
        if self.debug_info is True:
            print("\n[V. Memory] Prining v. memory with position '" + phex(address, 5) + "':")
        position = int(phex(address, 8), 0) - (4*3)

        for pos in range(position, position+(4*7), 4):
            if pos == position + (4*3):
                print("=====>\t", "[", phex(pos, 8), "] ~ ", self.virtual_memory[pos])
            else:
                print("\t", "[", phex(pos, 8), "] ~ ", self.virtual_memory[pos])

    def find_starting_address(self):
        """ Find the starting position of 'main:' label in virtual memory. """
        if self.debug_info is True:
            print("\n[V. Memory] Looking for starting address...")

        start_address = ""

        for index, instruction in enumerate(self.virtual_memory):
            if instruction is None:
                continue
            elif instruction[1] == "main:":
                start_address = phex(index, 8)
                break

        if self.debug_info is True:
            print("[V. Memory] Starting address found '" + start_address + "'...")

        return start_address

    def print_virtual_memory_layout(self):
        """ Print the contents of virtual memory.  """
        if self.debug_info is True:
            print("\n[V. Memory] Printing contents of virtual memory table...:")
        for index, data in enumerate(self.virtual_memory):
            if index == self.const_stack_start:
                print("[...] STACK(up): ")
            if index == self.const_heap_start:
                print("[...] HEAP(down):")
            if index == self.const_bss_start:
                print("[...] BSS:")
            if index == self.const_text_start:
                print("[...] TEXT (instructions):")

            if data != None:
                print("[...]", phex(index, 10), " ~ ", data)
Beispiel #3
0
class CPU:
    """ This class represents the CPU component. """
    def __init__(self, debug):
        """ Initialize the data members of the component. """
        self.debug_info = debug

        if self.debug_info is True:
            print("[CPU] initializing...")

        self.bus = Bus(debug)

        self.register_table = {
            # General purpose registers
            "rax": 0,
            "rbx": 0,
            "rcx": 0,
            "rdx": 0,
            "r8": 0,
            "r9": 0,
            # Special purposee registers
            "pc": 0,
            "rsp": 0,
            "rbp": 0,
            "rip": 0,
            "rflags": 0,
        }

        # Performance/timing variables
        self.current_instr_size = 0
        self.current_instr_cycles = 0
        self.cpi = 0

        ###########################

        if self.debug_info is True:
            print("[CPU] finished initializing...")

    def synchronize_with_memory(self, callee, callee_name):
        """
            Point the program counter to the first instruction to execute in virtual
            memory.
        """
        starting_position = self.bus.communicate(
            "cpu", callee, callee_name, "virtual memory, starting position",
            "")
        self.register_table["pc"] = int(starting_position, 0)

    def fetch_next_instruction(self, callee, callee_name):
        """
            Retrieve the instruction that is (size of instruction) positions from the current
            position pointed to by the program counter, then increment the counter that many
            positions.
        """
        instruction = self.bus.communicate("cpu", callee, callee_name,
                                           "cache, give block",
                                           self.register_table["pc"] + 4)
        self.register_table["pc"] = self.register_table["pc"] + 4

        # Cache missed
        if instruction == "MISS":
            return "CACHE MISSED"

        # parse instruction
        parsed = self.parse_instruction(instruction)

        return parsed

    def parse_instruction(self, instruction):
        """
            Seperate given instruction string into op, dest_reg, src_reg and value variables.
            Then package them together into a list and return it.
        """
        if self.debug_info is True:
            print("\n[CPU] parsing instruction '" + instruction + "'...")

        register_id = [
            "rax", "rbx", "rcx", "rdx", "r8", "r9", "rsp", "rbp", "rip"
        ]
        op = ""
        dest_reg = ""
        src_reg = ""
        value = ""

        # just a label, ignore
        if ':' in instruction:
            return "label"

        split_instr = instruction.split(',')
        if len(split_instr) == 1:
            print(split_instr)
            split_instr = split_instr[0].split(' ')
        else:
            split_instr[0] = split_instr[0].split(' ', 1)
            split_instr = [
                split_instr[0][0], split_instr[0][1],
                split_instr[1].lstrip(' ')
            ]

        if len(split_instr) == 2:
            value = split_instr[1]
        elif str(split_instr[2]) in register_id:
            src_reg = split_instr[2]
            dest_reg = split_instr[1]
        else:
            dest_reg = split_instr[1]
            value = split_instr[2]

        op = split_instr[0]

        if self.debug_info is True:
            print("[CPU] parsing done, result:")
            print("[...]   length:", len(split_instr))
            print("[...]    split:", split_instr)
            print("[...]       op: ", op)
            print("[...] dest_reg: ", dest_reg)
            print("[...]  src_reg: ", src_reg)
            print("[...]    value: ", value)

        parsed = {
            "op": op,
            "dest_reg": dest_reg,
            "src_reg": src_reg,
            "value": value
        }

        return parsed

    def ALU(self, op, dest_reg, src_reg, value, callee, callee_name):
        """
            ALU will apply the given operation 'op', to the registers given or perform
            special behaviors. Requires virtual memory communication to point to the
            appropriate place when a jump is executed.
        """
        if self.debug_info is True:
            print_alu_debug(op, dest_reg, src_reg, value)

        # Mov operation
        if op == "mov":
            # Check if moving value or register
            if value != "":
                self.register_table[dest_reg] = int(value, 0)
            else:
                self.register_table[dest_reg] = self.register_table[src_reg]

        # add
        if op == "add":
            # Check if adding value or register
            if value != "":
                self.register_table[dest_reg] += int(value, 0)
            else:
                self.register_table[dest_reg] += self.register_table[src_reg]

        # sub
        if op == "sub":
            # Check if adding value or register
            if value != "":
                self.register_table[dest_reg] -= int(value, 0)
            else:
                self.register_table[dest_reg] -= self.register_table[src_reg]

        # cmp
        if op == "cmp":
            self.register_table["rflags"] = (
                self.register_table[dest_reg] == self.register_table[src_reg])

        # jmp
        if op == "jmp":
            # self.bus.communicate("cpu", callee, callee_name,
            #                      "virtual memory, find position of label", value)
            self.register_table["pc"] = int(value, 0)

    def print_register_table(self):
        """ Print the value of all the registers. """
        print("[CPU] Printing register table...")
        print("[...] RAX    = ", phex(self.register_table["rax"], 32))
        print("[...] RBX    = ", phex(self.register_table["rbx"], 32))
        print("[...] RCX    = ", phex(self.register_table["rcx"], 32))
        print("[...] RDX    = ", phex(self.register_table["rdx"], 32))
        print("[...] R8     = ", phex(self.register_table["r8"], 32))
        print("[...] R9     = ", phex(self.register_table["r9"], 32))
        print("[...] PC     = ", phex(self.register_table["pc"], 32))
        print("[...] RSP    = ", phex(self.register_table["rsp"], 32))
        print("[...] RBP    = ", phex(self.register_table["rbp"], 32))
        print("[...] RIP    = ", phex(self.register_table["rip"], 32))
        print("[...] RFLAGS = ", phex(self.register_table["rflags"], 6))

    def print_program_with_position(self, callee, callee_name):
        """ Display the position of the program counter and program. """
        # defualt offset 4 positions before, 4 positions after
        self.bus.communicate("cpu", callee, callee_name,
                             "virtual memory, print position",
                             self.register_table["pc"])