OOO-Processor-Simulator / compare.py
compare.py
Raw
#!/usr/bin/env python3
from typing import List, Dict

import argparse
import json

RED = '\x1b[31m'
GREEN = '\x1b[36m'
RESET = '\x1b[0m'


parser = argparse.ArgumentParser()

parser.add_argument("input", help="The input JSON for comparison.", type=argparse.FileType("r"))
parser.add_argument("--reference", "-r", required=True, help="The reference JSON.", type=argparse.FileType("r"))

args = parser.parse_args()

INPUT = json.load(args.input)
REFERENCE = json.load(args.reference)

# INPUT is [{"ActiveList": [], "BusyBitTable": [bool], "DecodedPCs": int, "Exception": bool, "ExceptionPC": int, "FreeList": [int], "IntegerQueue": [{}], "PC": int, "PhysicalRegisterFile": [int], "RegisterMapTable": [int], }]
# ActiveList: [{"Done": bool, "Exception": bool, "LogicalDestination": int, "OldDestination": int, "PC": int}]
# IntegerQueue: set' [{"DestRegister": int, "OpAIsReady": bool, "OpARegTag": int, "OpAValue": int, "OpBIsReady": bool, "OpBRegTag": int, "OpBValue": int, "OpCode": str, "PC": int}]

# INPUT must be a list
if type(INPUT) != list:
    print(f"[{RED}Error{RESET}] The input JSON must be a list structure")
    exit(1)

# Reference should be always a list
if type(REFERENCE) != list:
    print("The reference JSON should be a list. Please check if you pick a wrong reference file.")
    print("If the reference fils is not wrong, please contact TA for more information.")
    exit(2)


def compareIntegerQueueEntry(i: dict, r:dict) -> bool:
    '''
        @return true if i and r are the same
    '''
    
    types = {
        "DestRegister": int,

        "OpAIsReady": bool,
        "OpARegTag": int,
        "OpAValue": int,

        "OpBIsReady": bool,
        "OpBRegTag": int,
        "OpBValue": int,

        "OpCode": str,

        "PC": int,
    }


    # Check the reference
    for n, t in types.items():
        if not n in r:
            print(f"The reference integer queue entry has wrong format. Missing term: {n}. Raw: {r}.")
            print("Please check if the reference file is correct.")
            exit(2)

        if type(r[n]) != t:
            print(f"Wrong type for integer queue entry {n}. Raw: {r}.")
            print("Please check if the reference file is correct.")
            exit(2)

    # Compare the entry
    for exact_entry in ["PC", "OpCode", "DestRegister", "OpAIsReady", "OpBIsReady"]:
        if not exact_entry in i:
            print(f"[{RED}Error{RESET}][IntegerQueue] Missing property for the integer queue entry (PC={i['PC']}): {exact_entry}.")
            return False
        
        # type check
        if type(i[exact_entry]) != type(r[exact_entry]):
            print(f"[{RED}Error{RESET}][IntegerQueue] Property type mismatched for the integer queue entry (PC={i['PC']}): {exact_entry} should be {type(r[exact_entry])}")
            return False

        # Compare
        if i[exact_entry] != r[exact_entry]:
            print(f"[{RED}Error{RESET}][IntegerQueue] Mismatched property in the integer queue entry (PC={i['PC']}): {exact_entry}.")
            if exact_entry == "PC":
                print(f"[{RED}Error{RESET}][IntegerQueue] A PC mismatching usually means you are missing entries for some instructions.")

            return False
            
        
    # Now check the operand
    for op in ["A", "B"]:
        if i[f"Op{op}IsReady"]:
            # Check the value
            if not f"Op{op}Value" in i:
                print(f"[{RED}Error{RESET}][IntegerQueue] Missing property for the integer queue entry (PC={i['PC']}): Op{op}Value.")
                return False
            if i[f"Op{op}Value"] != r[f"Op{op}Value"]:
                print(f"[{RED}Error{RESET}][IntegerQueue] Mismatched property in the integer queue entry (PC={i['PC']}): Op{op}Value.")
                return False
        else:
            if not f"Op{op}RegTag" in i:
                print(f"[{RED}Error{RESET}][IntegerQueue] Missing property for the integer queue entry (PC={i['PC']}): Op{op}Value.")
                return False
            if i[f"Op{op}RegTag"] != r[f"Op{op}RegTag"]:
                print(f"[{RED}Error{RESET}][IntegerQueue] Mismatched property in the integer queue entry (PC={i['PC']}): Op{op}Value.")
                return False

    return True


def compareIntegerQueue(i: List[Dict], r: List[Dict]) -> bool:
    # check whether each entry has the PC.

    for el in r:
        if not "PC" in el:
            print("All integer queue entry in reference output should have property 'PC'. ")
            print("Please check if the reference file is correct.")
            exit(2)

    for el in i:
        if not "PC" in el:
            print(f"[{RED}Error{RESET}][IntegerQueue] All integer queue entry should have property 'PC'. ")
            return False
        
    # Now, sort the list by the PC.
    i.sort(key=lambda x: x["PC"])
    r.sort(key=lambda x: x["PC"])

    # First, compare the element count
    if len(i) != len(r):
        print(f"[{RED}Error{RESET}][IntegerQueue] The number of elements in the integer queue is different from reference.")
        print(f"[{RED}Error{RESET}][IntegerQueue] This usually means you are missing some instructions, or you have more instructions retired later.")
        return False
    
    # Then, compare each element
    for idx in range(len(r)):
        if compareIntegerQueueEntry(i[idx], r[idx]) == False:
            return False
        
    return True


def compareActiveListEntry(i: dict, r: dict) -> bool:
    types = {
        "Done": bool,
        "Exception": bool,
        "LogicalDestination": int,
        "OldDestination": int,
        "PC": int
    }

    # Check the reference
    for idx, t in types.items():
        if not idx in r:
            print(f"Missing property in active list entry: {idx}. Raw:{r}")
            print("Please check if the reference file is correct.")
            exit(2)

        if type(r[idx]) != t:
            print(f"Wrong type in the active list entry: {idx}. Expectation: {t}")
            print("Please check if the reference file is correct.")
            print(2)
        
    # Now, check the value of the entry 
    for idx, t in types.items():
        if not idx in i:
            print(f"[{RED}Error{RESET}][ActiveList] Missing property in active list entry: {idx}")
            return False

        # check the type
        if type(i[idx]) != t:
            print(f"[{RED}Error{RESET}][ActiveList] Mismatched type for a property in an active list entry: {idx}")
            return False
        
        # Do comparison
        if i[idx] != r[idx]:
            print(f"[{RED}Error{RESET}][ActiveList] Mismatched value of a property in an active list entry: {idx}")
            return False

    return True


def compareActiveList(i: List[dict], r:List[dict]) -> bool:
    # Make sure their count is the same
    if len(i) != len(r):
        print("Mismatched active list size.")
        return False
    
    for idx in range(len(i)):
        if compareActiveListEntry(i[idx], r[idx]) == False:
            print(f"[{RED}Error{RESET}][ActiveList] Active list entry at index {idx} is different.")
            return False

    return True


def compareCycleData(i: dict, r: dict) -> bool:
    types = {
        "ActiveList": list, 
        "BusyBitTable": list, 
        "DecodedPCs": list, 
        "Exception": bool, 
        "ExceptionPC": int, 
        "FreeList": list,
        "IntegerQueue": list,
        "PC": int,
        "PhysicalRegisterFile": list,
        "RegisterMapTable": list
    }

    # First, make sure the reference has the correct entry and type.
    for n, t in types.items():
        if not n in r:
            print(f"Missing property in the cycle data in the reference input: {n}")
            print("Please check if the reference file is correct.")
            exit(2)

        if type(r[n]) != t:
            print(f"Wrong type of the property in the cycle data in the reference input: {n}. The expected type is {t}")
            print("Please check if the reference file is correct.")
            exit(2)

    types.pop("ExceptionPC")

    # Second, check user's input to make sure they have the correct type.
    for n, t in types.items():
        if not n in i:
            print(f"[{RED}Error{RESET}][CycleData] Missing property in the cycle data in the input: {n}")
            return False
        
        if type(i[n]) != t:
            print(f"[{RED}Error{RESET}][CycleData] Wrong type of the property in the cycle data in the input: {n}. The expected type is {t}")
            return False
        
    # Now, it is time to check each entry.
    if compareActiveList(i["ActiveList"], r["ActiveList"]) == False:
        print(f"[{RED}Error{RESET}][CycleData] Active list mismatched!")
        return False
    
    for directed_check in ["BusyBitTable", "DecodedPCs", "Exception", "PC", "PhysicalRegisterFile", "RegisterMapTable"]:
        if i[directed_check] != r[directed_check]:
            print(f"[{RED}Error{RESET}][CycleData] Property '{directed_check}' mismatched.")
            return False
    
    if set(i["FreeList"]) != set(r["FreeList"]):
        print(f"[{RED}Error{RESET}][CycleData] Free list mismatched!")
        return False
    
    if compareIntegerQueue(i["IntegerQueue"], r["IntegerQueue"]) == False:
        print(f"[{RED}Error{RESET}][CycleData] Integer queue mismatched!")
        return False
    
    if r["Exception"] == True:
        # compare ExceptionPC
        if not "ExceptionPC" in i:
            print(f"[{RED}Error{RESET}][CycleData] Missing property ExceptionPC.")
            return False
        
        if type(i["ExceptionPC"]) != int:
            print(f"[{RED}Error{RESET}][CycleData] Type mismatched: Property ExceptionPC should be an integer.")
            return False
        
        if r["ExceptionPC"] != i["ExceptionPC"]:
            print(f"[{RED}Error{RESET}][CycleData] Property Exception PC mismatched!")
            return False

    return True

# Now it is the final comparison
if len(INPUT) != len(REFERENCE):
    print(f"[{RED}Error{RESET}][CycleData] Cycle count mismatched!\n")
    exit(1)

for i in range(len(INPUT)):
    if compareCycleData(INPUT[i], REFERENCE[i]) == False:
        print(f"[{RED}Error{RESET}][CycleData] Cycle {i} data mismatched. Exit.\n")
        exit(1)

print(f"{GREEN}PASSED!{RESET}")