OOO-Processor-Simulator / src / datastructures.py
datastructures.py
Raw
import re
import copy
###########################
#### Instruction CLASS ####
###########################

class Instruction:
    def __init__(self, mnemonic, dest, opA, program_counter, opB, imm=None):
        self.pc = program_counter
        self.mnemonic = mnemonic
        self.dest = dest
        self.opA = opA
        self.opB = opB
        self.imm = imm

    def is_register(self, operand):
        return isinstance(operand, str) and operand.startswith('x')

    @classmethod
    def parse(cls, text, pc):
        parts=[]
        imm = None
        partss = text.split()
        for part in partss:
            parts.append(re.sub(",", "", part))
        mnemonic = parts[0]
        dest = int(parts[1][1:])
        opA = int(parts[2][1:])
        if mnemonic == "addi":
            imm = int(parts[3])
            opB = int(parts[3])
        else:
            opB = int(parts[3][1:])
        return cls(mnemonic, dest, opA, pc, opB, imm=imm)

    def rename(self, new_dest, register_map_table, physical_opA, physical_opB):
        renamed_instruction = copy.deepcopy(self)
        renamed_instruction.dest = new_dest
        renamed_instruction.opA = physical_opA
        
        if self.mnemonic == "addi":
            renamed_instruction.opB = self.imm
        else:
            renamed_instruction.opB = physical_opB

        return renamed_instruction

    def get_data(self):
        return [
        self.pc,
        self.mnemonic,
        self.dest,
        self.opA,
        self.opB,
        self.imm]
##################################
#### RenamedInstruction CLASS ####
##################################

class RenamedInstruction:
    def __init__(self, instruction, physical_destination):
        self.mnemonic = instruction.mnemonic
        self.logical_destination = instruction.dest
        self.physical_destination = physical_destination
        self.opA = instruction.opA
        self.opB = instruction.opB
        self.imm = instruction.imm
        self.pc = instruction.pc

    def is_register(self, operand):
        return isinstance(operand, str) and operand.startswith('x')

    @classmethod
    def from_instruction(cls, instruction, physical_destination):
        return cls(instruction, physical_destination)

    def update_destination(self, new_physical_destination):
        self.physical_destination = new_physical_destination

    def update_sources(self, register_map_table):
        if self.opA is not None and isinstance(self.opA, str):
            logical_source = self.opA
            physical_source = register_map_table.get_physical_register(logical_source)
            self.opA = physical_source

        if self.opB is not None and isinstance(self.opB, str):
            logical_source = self.opB
            physical_source = register_map_table.get_physical_register(logical_source)
            self.opB = physical_source

################################
#### PhysicalRegister CLASS ####
################################

class PhysicalRegisterFile:
    def __init__(self, num_registers):
        self.registers = [0] * num_registers
        self.ready_flags = [True] * num_registers

    def write(self, index, value):
        self.registers[index] = value
        self.ready_flags[index] = True

    def read(self, index):
        return self.registers[index]

    def is_ready(self, index):
        return self.ready_flags[index]

    def mark_busy(self, index):
        self.ready_flags[index] = False

    def mark_ready(self, index):
        self.ready_flags[index] = True

    def has_free_registers(self):
        return any(flag for flag in self.ready_flags)

    def get_data(self):
        return self.registers

################################
#### ActiveList ENTRY CLASS ####
################################

class ActiveListEntry:
    def __init__(self, done=False, exception=False, physical_destination=0,  logical_destination=0, old_destination=0, pc=0,):
        self.done = done
        self.exception = exception
        self.logical_destination = logical_destination
        self.old_destination = old_destination
        self.pc = pc
        self.physical_destination = physical_destination

    def to_dict(self):
        return {
            "Done": self.done,
            "Exception": self.exception,
            "LogicalDestination": self.logical_destination,
            "OldDestination": self.old_destination,
            "PC": self.pc
        }

###########################
#### Active List CLASS ####
###########################
class ActiveList:
    def __init__(self):
        self.entries = []

    def get_last_entry(self):
        if len(self.entries) == 0:
            return None
        return self.entries[-1]

    def remove_last_entry(self):
        if len(self.entries) == 0:
            return
        self.entries.pop()

    def is_empty(self):
        return len(self.entries)==0

    def add_entry(self, entry):
        self.entries.append(entry)

    def remove_entry(self, entry_to_remove_pc):
        index = None
        for i, entry in enumerate(self.entries):
            if entry.pc == entry_to_remove_pc:
                index = i
                break

        if index is not None:
            self.entries.pop(index)

    def has_capacity(self):
        return len(self.entries) < 32
    
    def get_entry_by_logical_dest(self, logical_dest):
        for entry in self.entries:
            if entry.logical_destination == logical_dest:
                return entry
        return None

    def get_entry_by_pc(self, pc):
        for entry in self.entries:
            if entry.pc == pc:
                return entry
        return None

    def get_data(self):
        return [entry.to_dict() for entry in self.entries]

    def get_entries(self):
        return self.entries
    
    def clear_busy_bits(self, physical_register):
        for entry in self.entries:
            if entry.old_destination == physical_register:
                entry.done = True
                entry.exception = False


##################################
#### GERQUEUE ENTRY CLASS ####
##################################
class IntegerQueueEntry:
    def __init__(self, dest_register, op_code, pc, op_a_is_ready=False, op_a_reg_tag=0, op_a_value=0,
                 op_b_is_ready=False, op_b_reg_tag=0, op_b_value=0):
        self.dest_register = dest_register
        self.op_a_is_ready = op_a_is_ready
        self.op_a_reg_tag = op_a_reg_tag
        self.op_a_value = op_a_value
        self.op_b_is_ready = op_b_is_ready
        self.op_b_reg_tag = op_b_reg_tag
        self.op_b_value = op_b_value
        self.op_code = op_code
        self.pc = pc

    def to_dict(self):
        return {
            "DestRegister": self.dest_register,
            "OpAIsReady": self.op_a_is_ready,
            "OpARegTag": self.op_a_reg_tag,
            "OpAValue": self.op_a_value,
            "OpBIsReady": self.op_b_is_ready,
            "OpBRegTag": self.op_b_reg_tag,
            "OpBValue": self.op_b_value,
            "OpCode": self.op_code,
            "PC": self.pc
        }


############################
#### INTEGERQUEUE CLASS ####
############################
class IntegerQueue:
    def __init__(self):
        self.entries = []

    def add_entry(self, entry):
        self.entries.append(entry)

    def remove_entry(self, index):
        self.entries.pop(index)

    def remove_entry_by_pc(self, pc):
        self.entries = [entry for entry in self.entries if entry.pc != pc]

    def get_entry_by_pc(self, pc):
        for entry in self.entries:
            if entry.pc == pc:
                return entry
        return None

    def get_entry(self, index):
        return self.entries[index]

    def get_entry_by_dest_register(self, dest_register):
        for entry in self.entries:
            if entry.dest_register == dest_register:
                return entry
        return None

    def update_entry_ready(self, dest_register, op_a_ready, op_b_ready):
        entry = self.get_entry_by_dest_register(dest_register)
        if entry is not None:
            entry.op_a_is_ready = op_a_ready
            entry.op_b_is_ready = op_b_ready

    def has_capacity(self):
        return len(self.entries) < 32

    def get_data(self):
        return self.entries.copy()
############################
#### BusyBitTable CLASS ####
############################
class BusyBitTable:
    def __init__(self, size):
        self.bits = [False] * size
        self.instructions = [None] * size
    
    def get_data(self):
        return self.bits

    def set_busy(self, index, instruction):
        self.bits[index] = True
        self.instructions[index] = instruction

    def set_free(self, index):
        self.bits[index] = False
        self.instructions[index] = None

    def is_busy(self, physical_register):
        return self.bits[physical_register]

    def get_busy_instruction(self, physical_register):
        return self.instructions[physical_register]

    def get_physical_register(self, reg_tag, register_map_table):
        physical_register = register_map_table.get_physical_register(reg_tag)
        return physical_register

    def set_busy_bits(self, instruction, register_map_table):
        # Set busy bits for source operands
        if instruction.opA and instruction.is_register(instruction.opA):
            physical_source = register_map_table.get_physical_register(instruction.opA)
            self.set_busy(physical_source, instruction)
        if instruction.opB and instruction.is_register(instruction.opB):
            physical_source = register_map_table.get_physical_register(instruction.opB)
            self.set_busy(physical_source, instruction)

    def has_capacity(self, instruction, register_map_table):
        # Check if all source operands are available
        if instruction.opA and instruction.is_register(instruction.opA) and self.is_busy(register_map_table.get_physical_register(instruction.opA)):
            return False
        if instruction.opB and instruction.is_register(instruction.opB) and self.is_busy(register_map_table.get_physical_register(instruction.opB)):
            return False
        return True

###############################
#### Program Counter CLASS ####
###############################
class ProgramCounter:
    def __init__(self):
        self.value = 0

    def get_data(self):
        return self.value

    def increment(self, amount=1):
        self.value += amount

    def set(self, val):
        self.value = val


########################
#### FreeList CLASS ####
########################
class FreeList:
    def __init__(self):
        self.free_registers = list(range(32, 64))

    def get_data(self):
        return self.free_registers

    def remove_register(self, register_index):
        self.free_registers.remove(register_index)

    def allocate(self):
        if not self.free_registers:
            raise Exception("No free registers available")
        return self.free_registers.pop(0)

    def free(self, reg):
        self.free_registers.append(reg)

    def get(self):
        if not self.free_registers:
            raise Exception("No more free registers in the free list")
        return self.free_registers.pop(0)
    
    def has_capacity(self):
        return bool(self.free_registers)

################################
#### RegisterMapTable CLASS ####
################################
class RegisterMapTable:
    def __init__(self):
        self.mapping = list(range(32))

    def get_data(self):
        return self.mapping

    def add_entry(self, logical_register, physical_register):
        if 0 <= logical_register <= 31:
            self.mapping[logical_register] = physical_register
        else:
            raise Exception(f"Invalid architectural register {logical_register}")

    def update_mapping(self, logical_register, physical_register):
        if 0 <= logical_register <= 31:
            self.mapping[logical_register] = physical_register
        else:
            raise Exception(f"Invalid architectural register {logical_register}")


    def get_physical_register(self, logical_register):
        if 0 <= logical_register <= 31:
            return self.mapping[logical_register]
        else:
            raise Exception(f"Invalid architectural register {logical_register}")

    def get_logical_register(self, physical_register):
        for i in range(len(self.mapping)):
            if self.mapping[i] == physical_register:
                return i


##########################
#### DecodedPCS CLASS ####
##########################
class DecodedPCs:
    def __init__(self):
        self.pc_values = []

    def get_data(self):
        return self.pc_values

    def add_pc(self, pc):
        self.pc_values.append(pc)

    def set_data(self, pc_values):
        self.pc_values = pc_values

    def remove_pc(self, pc):
        if pc in self.pc_values:
            self.pc_values.remove(pc)
        else:
            raise Exception(f"PC value {pc} not found in DecodedPCs")


#############################
#### ExceptionFlag CLASS ####
#############################
class ExceptionFlag:
    def __init__(self):
        self.flag = False

    def set(self):
        self.flag = True

    def clear(self):
        self.flag = False

    def is_set(self):
        return self.flag

    def get_data(self):
        return self.flag

############################
#### ExceptionPCS CLASS ####
############################
class ExceptionPC:
    def __init__(self):
        self.pc = 0

    def set(self, pc):
        self.pc = pc

    def get_data(self):
        return self.pc