#!/usr/bin/python3
# -*- coding: utf-8 -*-
import collections
import json
import sys
import os
from pathlib import Path
from fnmatch import fnmatch
from datetime import datetime
import random
from pathlib import Path
import shutil
from subprocess import PIPE, run
import threading
import time

iverilog = True
vcs = False
coverage = False
zip_waves = True
caravan = False 
def go_up(path, n):
    for i in range(n):
        path = os.path.dirname(path)
    return path
# search pattern in file
def search_str(file_path, word):
    with open(file_path, 'r') as file:
        # read all content of a file
        content = file.read()
        # check if string present in a file
        if word in content:
            return "passed"
        else:
            return "failed"

def change_str(str,new_str,file_path):
    # Read in the file
    with open(file_path, 'r') as file :
        filedata = file.read()

    filedata = filedata.replace(str, new_str)

    # Write the file out again
    with open(file_path, 'w') as file:
        file.write(filedata)

class RunTest:
    def __init__(self,test_name,sim,corner) -> None:
        self.cocotb_path = f"{os.getenv('CARAVEL_ROOT')}/verilog/dv/cocotb"
        self.test_name = test_name
        self.sim_type  = sim
        self.corner  = corner
        self.create_log_file()
        self.hex_generate()
        self.runTest()
    
    # create and open full terminal log to be able to use it before run the test
    def create_log_file(self):
        self.cd_cocotb()
        os.chdir(f"sim/{os.getenv('RUNTAG')}")
        test_dir = f"{self.sim_type}-{self.test_name}"
        if (self.sim_type == "GL_SDF"):
            test_dir = f'{test_dir}-{self.corner}'
        os.makedirs(f"{test_dir}",exist_ok=True)
        self.cd_cocotb()
        self.sim_path = f"sim/{os.getenv('RUNTAG')}/{test_dir}/"
        terminal_log=f"{self.sim_path}/fullTerminal.log"
        test_log=f"{self.sim_path}/{self.test_name}.log"
        self.test_log=open(test_log, "w")
        self.full_file=f"{self.sim_path}/full.log"
        self.full_terminal = open(self.full_file, "w")
        
    def runTest(self):
        self.full_test_name = f"{self.sim_type}-{self.test_name}"
        if (self.sim_type=="GL_SDF"):
            self.full_test_name =  f"{self.sim_type}-{self.test_name}-{self.corner}"
        os.environ["TESTFULLNAME"] = f"{self.full_test_name}"
        if (iverilog):return self.runTest_iverilog()
        elif(vcs): return self.runTest_vcs()

    # iverilog function
    def runTest_iverilog(self):
        print(f"Start running test: {self.sim_type}-{self.test_name}")
        CARAVEL_ROOT = os.getenv('CARAVEL_ROOT')
        CARAVEL_VERILOG_PATH  = os.getenv('CARAVEL_VERILOG_PATH')
        MCW_ROOT = os.getenv('MCW_ROOT')
        VERILOG_PATH = os.getenv('VERILOG_PATH')
        CARAVEL_PATH = os.getenv('CARAVEL_PATH')
        USER_PROJECT_VERILOG  = os.getenv('USER_PROJECT_VERILOG')
        FIRMWARE_PATH = os.getenv('FIRMWARE_PATH')
        RUNTAG = os.getenv('RUNTAG')
        ERRORMAX = os.getenv('ERRORMAX')
        PDK_ROOT = os.getenv('PDK_ROOT')
        PDK = os.getenv('PDK')
        TESTFULLNAME = os.getenv('TESTFULLNAME')
        env_vars = f"-e {CARAVEL_ROOT} -e CARAVEL_VERILOG_PATH={CARAVEL_VERILOG_PATH} -e MCW_ROOT={MCW_ROOT} -e VERILOG_PATH={VERILOG_PATH} -e CARAVEL_PATH={CARAVEL_PATH} -e USER_PROJECT_VERILOG={USER_PROJECT_VERILOG} -e FIRMWARE_PATH={FIRMWARE_PATH} -e RUNTAG={RUNTAG} -e ERRORMAX={ERRORMAX} -e PDK_ROOT={PDK_ROOT} -e PDK={PDK} -e TESTFULLNAME={TESTFULLNAME}"
        macros = f'-DFUNCTIONAL -DSIM=\\\"{self.sim_type}\\\"  -DUSE_POWER_PINS -DUNIT_DELAY=#1 -DMAIN_PATH=\\\"{self.cocotb_path}\\\" -DIVERILOG -DTESTNAME=\\\"{self.test_name}\\\" -DTAG=\\\"{RUNTAG}\\\" '
        if self.test_name == "la":
            macros = f'{macros} -DLA_TESTING'
        if self.test_name in ["gpio_all_o_user","gpio_all_i_user","gpio_all_i_pu_user","gpio_all_i_pd_user","gpio_all_bidir_user"]:
            macros = f'{macros} -DGPIO_TESTING'
        if(self.sim_type=="RTL"): 
            includes = f"-f {VERILOG_PATH}/includes/includes.rtl.caravel"
        elif(self.sim_type=="GL"): 
            macros = f'{macros}  -DGL'
            includes = f"-f {VERILOG_PATH}/includes/includes.gl.caravel"
        elif(self.sim_type=="GLSDF"): 
            print(f"iverilog can't run SDF for test {self.test_name} Please use anothor simulator like cvc" )
            return
        user_project = f"{CARAVEL_PATH}/rtl/__user_project_wrapper.v {CARAVEL_PATH}/rtl/__user_project_gpio_example.v {CARAVEL_PATH}/rtl/__user_project_la_example.v"
        if caravan:
            print ("Use caravan")
            macros = f'-DCARAVAN {macros} '
            user_project = f"{CARAVEL_PATH}/rtl/__user_analog_project_wrapper.v"
        iverilog_command = (f"iverilog -Ttyp {macros} {includes}  -o {self.sim_path}/sim.vvp"
                            f" {user_project}  caravel_top.sv"
                            f" && TESTCASE={self.test_name} MODULE=caravel_tests vvp -M $(cocotb-config --prefix)/cocotb/libs -m libcocotbvpi_icarus {self.sim_path}/sim.vvp")
        docker_command = f"docker run -it {env_vars} -v {os.getenv('CARAVEL_ROOT')}:{os.getenv('CARAVEL_ROOT')} -v {os.getenv('MCW_ROOT')}:{os.getenv('MCW_ROOT')} -v {os.getenv('PDK_ROOT')}:{os.getenv('PDK_ROOT')}   efabless/dv:cocotb sh -c 'cd {self.cocotb_path} && {iverilog_command}' >> {self.full_file}"
        self.full_terminal = open(self.full_file, "a")
        self.full_terminal.write(f"docker command for running iverilog and cocotb:\n% ")
        self.full_terminal.write(os.path.expandvars(docker_command)+"\n")
        self.full_terminal.close()
        
        os.system(docker_command)
        self.passed = search_str(self.test_log.name,"Test passed with (0)criticals (0)errors")
        Path(f'{self.sim_path}/{self.passed}').touch()
        if self.passed == "passed": 
            print(f"Test: {self.sim_type}-{self.test_name} passed")
        else : 
            print(f"Test: {self.sim_type}-{self.test_name} Failed please check logs under {self.sim_path}")


    # vcs function      
    def runTest_vcs(self):
        print(f"Start running test: {self.sim_type}-{self.test_name}")
        CARAVEL_PATH = os.getenv('CARAVEL_PATH')
        PDK_ROOT = os.getenv('PDK_ROOT')
        PDK = os.getenv('PDK')
        VERILOG_PATH = os.getenv('VERILOG_PATH')
        dirs = f'+incdir+\\\"{PDK_ROOT}/{PDK}\\\" '
        if self.sim_type == "RTL":
            shutil.copyfile(f'{VERILOG_PATH}/includes/rtl_caravel_vcs.v', f"{self.cocotb_path}/includes.v")
            change_str(str="\"caravel_mgmt_soc_litex/verilog",new_str=f"\"{VERILOG_PATH}",file_path=f"{self.cocotb_path}/includes.v")
            change_str(str="\"caravel/verilog",new_str=f"\"{CARAVEL_PATH}",file_path=f"{self.cocotb_path}/includes.v")
        else: 
            dirs = f' {dirs} -f \\\"{VERILOG_PATH}/includes/gl_caravel_vcs.list\\\" '
        macros = f'+define+FUNCTIONAL +define+USE_POWER_PINS +define+UNIT_DELAY=#1 +define+MAIN_PATH=\\\"{self.cocotb_path}\\\" +define+VCS '
        if self.test_name == "la":
            macros = f'{macros} +define+LA_TESTING'
        if self.test_name in ["gpio_all_o_user","gpio_all_i_user","gpio_all_i_pu_user","gpio_all_i_pd_user","gpio_all_bidir_user"]:
            macros = f'{macros} +define+GPIO_TESTING'
        # shutil.copyfile(f'{self.test_full_dir}/{self.test_name}.hex',f'{self.sim_path}/{self.test_name}.hex')
        # if os.path.exists(f'{self.test_full_dir}/test_data'):
        #     shutil.copyfile(f'{self.test_full_dir}/test_data',f'{self.sim_path}/test_data')
        if (self.sim_type=="GL_SDF"):
            macros = f'{macros} +define+ENABLE_SDF +define+SIM=GL_SDF +define+GL +define+SDF_POSTFIX=\\\"{self.corner[-1]}{self.corner[-1]}\\\" +define+CORNER=\\\"{self.corner[0:3]}\\\"'
            # corner example is corner nom-t so `SDF_POSTFIX = tt and `CORNER = nom
            # os.makedirs(f"annotation_logs",exist_ok=True)
            dirs = f"{dirs}  +incdir+\\\"{os.getenv('MCW_ROOT')}/verilog/\\\" "
            # +incdir+\\\"{os.getenv('CARAVEL_ROOT')}/signoff/caravel/primetime-signoff/\\\"
        elif(self.sim_type=="GL"): 
            macros = f'{macros}  +define+GL  +define+SIM=GL'
        elif (self.sim_type=="RTL"): 
            macros = f'{macros} +define+SIM=\\\"RTL\\\"'
        else: 
            print(f"Fatal: incorrect simulation type {self.sim_type}")
        coverage_command = ""
        if coverage: 
            coverage_command = "-cm line+tgl+cond+fsm+branch+assert"
        os.environ["TESTCASE"] = f"{self.test_name}"
        os.environ["MODULE"] = f"caravel_tests"
        os.environ["SIM"] = self.sim_type
        user_project = f"-v {CARAVEL_PATH}/rtl/__user_project_wrapper.v"
        if caravan:
            print ("Use caravan")
            macros = f'+define+CARAVAN {macros} '
            user_project = f"-v {CARAVEL_PATH}/rtl/__user_analog_project_wrapper.v"
        os.system(f"vlogan -full64  -sverilog +error+30 caravel_top.sv {user_project} {dirs}  {macros} +define+TESTNAME=\\\"{self.test_name}\\\" +define+FTESTNAME=\\\"{self.full_test_name}\\\" +define+TAG=\\\"{os.getenv('RUNTAG')}\\\" -l {self.sim_path}/analysis.log -o {self.sim_path} ")

        os.system(f"vcs +lint=TFIPC-L {coverage_command} +error+30 -R -diag=sdf:verbose +sdfverbose +neg_tchk -debug_access -full64  -l {self.sim_path}/test.log  caravel_top -Mdir={self.sim_path}/csrc -o {self.sim_path}/simv +vpi -P pli.tab -load $(cocotb-config --lib-name-path vpi vcs)")
        self.passed = search_str(self.test_log.name,"Test passed with (0)criticals (0)errors")
        Path(f'{self.sim_path}/{self.passed}').touch()
        os.system("rm -rf AN.DB ucli.key core") # delete vcs additional files
        #delete wave when passed
        if self.passed == "passed" and zip_waves:
            os.chdir(f'{self.cocotb_path}/{self.sim_path}')
            os.system(f'zip -m waves_logs.zip analysis.log test.log *.vpd *.vcd')
            self.cd_cocotb()
        if os.path.exists(f"{self.cocotb_path}/sdfAnnotateInfo"):
            shutil.move(f"{self.cocotb_path}/sdfAnnotateInfo", f"{self.sim_path}/sdfAnnotateInfo")
        shutil.copyfile(f'{self.cocotb_path}/hex_files/{self.test_name}.hex',f'{self.sim_path}/{self.test_name}.hex')

    def find(self,name, path):
        for root, dirs, files in os.walk(path):
            if name in files:
                return os.path.join(root, name)
        print(f"Test {name} doesn't exist or don't have a C file ")

    def test_path(self):
        test_name = self.test_name
        test_name += ".c"
        tests_path = os.path.abspath(f"{self.cocotb_path}/tests")
        test_file =  self.find(test_name,tests_path)
        test_path = os.path.dirname(test_file)
        return (test_path)

    def hex_generate(self):
        tests_use_dff2 = ["mem_dff"]
        tests_use_dff = ["mem_dff2","debug"]
        #open docker 
        test_path =self.test_path()
        self.cd_make()
        if not os.path.exists(f"{self.cocotb_path}/hex_files"):
            os.makedirs(f"{self.cocotb_path}/hex_files") # Create a new hex_files directory because it does not exist 
        elf_out = f"{self.cocotb_path}/hex_files/{self.test_name}.elf"
        c_file = f"{test_path}/{self.test_name}.c"
        hex_file = f"{self.cocotb_path}/hex_files/{self.test_name}.hex"
        GCC_PATH = "/foss/tools/riscv-gnu-toolchain-rv32i/217e7f3debe424d61374d31e33a091a630535937/bin/"
        GCC_PREFIX = "riscv32-unknown-linux-gnu"
        SOURCE_FILES = f"{os.getenv('FIRMWARE_PATH')}/crt0_vex.S {os.getenv('FIRMWARE_PATH')}/isr.c"
        LINKER_SCRIPT = f"{os.getenv('FIRMWARE_PATH')}/sections.lds"
        CPUFLAGS = f"-march=rv32i -mabi=ilp32 -D__vexriscv__ "
        verilog_path = f"{os.getenv('VERILOG_PATH')}"
        test_dir = f"{os.getenv('VERILOG_PATH')}/dv/tests-caravel/mem" # linker script include // TODO: to fix this in the future from the mgmt repo
        #change linker script to for mem tests 
        if self.test_name in tests_use_dff2:
           LINKER_SCRIPT = self.linkerScript_for_mem("dff2",LINKER_SCRIPT)
        elif self.test_name in tests_use_dff:
           LINKER_SCRIPT = self.linkerScript_for_mem("dff",LINKER_SCRIPT)

        elf_command = (f"{GCC_PATH}/{GCC_PREFIX}-gcc -g -I{verilog_path}/dv/firmware -I{verilog_path}/dv/generated  -I{verilog_path}/dv/ "
                 f"-I{verilog_path}/common {CPUFLAGS} -Wl,-Bstatic,-T,{LINKER_SCRIPT},"
                 f"--strip-debug -ffreestanding -nostdlib -o {elf_out} {SOURCE_FILES} {c_file}")
        hex_command = f"{GCC_PATH}/{GCC_PREFIX}-objcopy -O verilog {elf_out} {hex_file} "
        sed_command = f"sed -ie 's/@10/@00/g' {hex_file}"
       
        hex_gen_state = os.system(f"docker run -it -v {go_up(self.cocotb_path,4)}:{go_up(self.cocotb_path,4)}  efabless/dv:latest sh -c 'cd {test_dir} && {elf_command} && {hex_command} && {sed_command} '")
        self.full_terminal.write("elf file generation command:\n% ")
        self.full_terminal.write(os.path.expandvars(elf_command)+"\n")
        self.full_terminal.write("hex file generation command:\n% ")
        self.full_terminal.write(os.path.expandvars(hex_command)+"\n% ")
        self.full_terminal.write(os.path.expandvars(sed_command)+"\n")
        self.cd_cocotb()
        self.full_terminal.close()
        if hex_gen_state != 0 :
            print(f"fatal: Error when generating hex")
            sys.exit()
        
    #change linker script to for mem tests 
    def linkerScript_for_mem(self,ram,LINKER_SCRIPT):
        new_LINKER_SCRIPT = f"{self.cocotb_path}/{self.sim_path}/sections.lds"
        shutil.copyfile(LINKER_SCRIPT, new_LINKER_SCRIPT)
        if ram == "dff2":  
            change_str(str="> dff ",new_str="> dff2 ",file_path=new_LINKER_SCRIPT)
            change_str(str="> dff\n",new_str="> dff2\n",file_path=new_LINKER_SCRIPT)
            change_str(str="ORIGIN(dff)",new_str="ORIGIN(dff2)",file_path=new_LINKER_SCRIPT)
            change_str(str="LENGTH(dff)",new_str="LENGTH(dff2)",file_path=new_LINKER_SCRIPT)
        elif ram == "dff":
            change_str(str="> dff2 ",new_str="> dff ",file_path=new_LINKER_SCRIPT)
            change_str(str="ORIGIN(dff2)",new_str="ORIGIN(dff)",file_path=new_LINKER_SCRIPT)
            change_str(str="LENGTH(dff2)",new_str="LENGTH(dff)",file_path=new_LINKER_SCRIPT)
        else: 
            print(f"ERROR: wrong trype of ram {ram} need to be used for now the oldy rams that can be used for flashing and data are dff and dff2")
            sys.exit()
        return new_LINKER_SCRIPT

    def cd_make(self):
        os.chdir(f"{os.getenv('VERILOG_PATH')}/dv/make")
        
    def cd_cocotb(self):
        os.chdir(self.cocotb_path)

class RunRegression: 
    def __init__(self,regression,test,type_arg,testlist,corner) -> None:
        self.cocotb_path = f"{os.getenv('CARAVEL_ROOT')}/verilog/dv/cocotb"
        self.regression_arg = regression
        self.test_arg = test
        self.testlist_arg = testlist
        self.corners = corner
        self.total_start_time = datetime.now()
        if type_arg is None:
            type_arg = "RTL"
        self.type_arg = type_arg
        self.write_command_log()
        self.write_git_log()
        with open('tests.json') as f:
            self.tests_json = json.load(f)
            self.tests_json = self.tests_json["Tests"]
        self.get_tests()
        self.run_regression()

    def get_tests(self):
        self.tests = collections.defaultdict(lambda : collections.defaultdict(lambda : collections.defaultdict(dict))) #key is testname and value is list of sim types
        self.unknown_tests = 0
        self.passed_tests = 0
        self.failed_tests = 0
        # regression 
        if self.regression_arg is not None:
            sim_types = ("RTL","GL","GL_SDF")
            for test,test_elements in self.tests_json.items():
                if fnmatch(test,"_*"):
                        continue
                for sim_type in sim_types:
                    if sim_type =="GL_SDF": 
                        for corner in self.corners: 
                            if self.regression_arg in test_elements[sim_type]: 
                                self.add_new_test(test_name=test,sim_type = sim_type,corner = corner)
                    else: 
                        if self.regression_arg in test_elements[sim_type]: 
                                self.add_new_test(test_name=test,sim_type = sim_type,corner = "-")
            if (len(self.tests)==0):
                print(f"fatal:{self.regression_arg} is not a valid regression name please input a valid regression \ncheck tests.json for more info")
                sys.exit()
        #test
        if self.test_arg is not None:
            if isinstance(self.test_arg,list):
                for test in self.test_arg:
                    if test in self.tests_json:
                        if isinstance(self.type_arg,list):
                            for sim_type in self.type_arg:
                                if sim_type =="GL_SDF": 
                                    for corner in self.corners: 
                                        self.add_new_test(test_name=test,sim_type = sim_type, corner = corner)
                                else: self.add_new_test(test_name=test,sim_type = sim_type,corner = "-")
                        else:
                            if sim_type =="GL_SDF": 
                                for corner in self.corners: 
                                    self.add_new_test(test_name=test,sim_type = sim_type, corner = corner)
                            else: self.add_new_test(test_name=test,sim_type = sim_type,corner = "-")

            else:
                if self.test_arg in self.tests_json:
                    if isinstance(self.type_arg,list):
                        for sim_type in self.type_arg:
                            self.add_new_test(test_name=self.test_arg,sim_type = sim_type)
                    else:
                        self.add_new_test(test_name=self.test_arg,sim_type = self.type_arg)
        # testlist TODO: add logic for test list
        if self.testlist_arg is not None:
            print(f'fatal: code for test list isnt added yet')
            sys.exit()


        self.update_reg_log()

    def add_new_test(self,test_name,sim_type,corner):
        self.tests[test_name][sim_type][corner]["status"]= "pending"
        self.tests[test_name][sim_type][corner]["starttime"]= "-"
        self.tests[test_name][sim_type][corner]["endtime"]= "-"
        self.tests[test_name][sim_type][corner]["duration"] = "-"
        self.tests[test_name][sim_type][corner]["pass"]= "-"
        self.unknown_tests +=1

    def run_regression(self):
        threads = list()
        for test,sim_types in self.tests.items():
            for sim_type,corners in sim_types.items(): # TODO: add multithreading or multiprocessing here
                for corner,status in corners.items():
                    if iverilog: #threading
                        # x = threading.Thread(target=self.test_run_function,args=(test,sim_type,corner))
                        # threads.append(x)
                        # x.start()
                        # time.sleep(10)
                        self.test_run_function(test,sim_type,corner)
                    else: 
                        self.test_run_function(test,sim_type,corner)
        for index, thread in enumerate(threads):
            thread.join()

        if coverage:
            self.generate_cov()
        #TODO: add send mail here
    
    def test_run_function(self,test,sim_type,corner):
        start_time = datetime.now()
        self.tests[test][sim_type][corner]["starttime"] = datetime.now().strftime("%H:%M:%S(%a)")
        self.tests[test][sim_type][corner]["duration"] = "-"
        self.tests[test][sim_type][corner]["status"] = "running"
        self.update_reg_log()
        test_run = RunTest(test,sim_type,corner,)
        self.tests[test][sim_type][corner]["status"] = "done"
        self.tests[test][sim_type][corner]["endtime"] = datetime.now().strftime("%H:%M:%S(%a)")
        self.tests[test][sim_type][corner]["duration"] = ("%.10s" % (datetime.now() - start_time))
        self.tests[test][sim_type][corner]["pass"]= test_run.passed
        if test_run.passed == "passed":
            self.passed_tests +=1
        elif test_run.passed == "failed":
            self.failed_tests +=1
        self.unknown_tests -=1
        self.update_reg_log()


    def generate_cov(self):
        os.chdir(f"{self.cocotb_path}/sim/{os.getenv('RUNTAG')}")
        os.system(f"urg -dir RTL*/*.vdb -format both -show tests -report coverageRTL/")
        # os.system(f"urg -dir GL*/*.vdb -format both -show tests -report coverageGL/")
        # os.system(f"urg -dir SDF*/*.vdb -format both -show tests -report coverageSDF/")
        os.chdir(self.cocotb_path)

    def update_reg_log(self):
        file_name=f"sim/{os.getenv('RUNTAG')}/runs.log"
        f = open(file_name, "w")
        f.write(f"{'Test':<33} {'status':<10} {'start':<15} {'end':<15} {'duration':<13} {'p/f':<5}\n")
        for test,sim_types in self.tests.items():
            for sim_type,corners in sim_types.items():
                for corner,status in corners.items():
                    new_test_name= f"{sim_type}-{test}-{corner}"
                    f.write(f"{new_test_name:<33} {status['status']:<10} {status['starttime']:<15} {status['endtime']:<15} {status['duration']:<13} {status['pass']:<5}\n")
        f.write(f"\n\nTotal: ({self.passed_tests})passed ({self.failed_tests})failed ({self.unknown_tests})unknown  ({('%.10s' % (datetime.now() - self.total_start_time))})time consumed ")
        f.close()
    
    def write_command_log(self):
        file_name=f"sim/{os.getenv('RUNTAG')}/command.log"
        f = open(file_name, "w")
        f.write(f"{' '.join(sys.argv)}")
        f.close()
  
    def write_git_log(self):
        file_name=f"sim/{os.getenv('RUNTAG')}/git_show.log"
        f = open(file_name, "w")
        # status, output = commands.getstatusoutput("git show")
        f.write( f"Repo: {run('basename -s .git `git config --get remote.origin.url`', stdout=PIPE, stderr=PIPE, universal_newlines=True, shell=True).stdout}")
        f.write( f"Branch name: {run('git symbolic-ref --short HEAD', stdout=PIPE, stderr=PIPE, universal_newlines=True, shell=True).stdout}")
        f.write( run('git show --quiet HEAD', stdout=PIPE, stderr=PIPE, universal_newlines=True, shell=True).stdout)
        MCW_ROOT = f"MCW_ROOT"

        f.write( f"\n\nRepo: {run(f'cd {os.getenv(MCW_ROOT)};basename -s .git `git config --get remote.origin.url`', stdout=PIPE, stderr=PIPE, universal_newlines=True, shell=True).stdout}")
        f.write( f"Branch name: {run(f'cd {os.getenv(MCW_ROOT)};git symbolic-ref --short HEAD', stdout=PIPE, stderr=PIPE, universal_newlines=True, shell=True).stdout}")
        f.write( run(f'cd {os.getenv(MCW_ROOT)};git show --quiet HEAD', stdout=PIPE, stderr=PIPE, universal_newlines=True, shell=True).stdout)
        f.close()
class main():
    def __init__(self,args) -> None:
        self.regression = args.regression
        self.test       = args.test
        self.testlist   = args.testlist
        self.sim       = args.sim
        self.tag        = args.tag
        self.corner        = args.corner
        self.maxerr        = args.maxerr
        self.check_valid_args()
        self.set_tag()
        self.def_env_vars()
        RunRegression(self.regression,self.test,self.sim,self.testlist,self.corner)

    def check_valid_args(self):
        if all(v is  None for v in [self.regression, self.test, self.testlist]):
            print ("Fatal: Should provide at least one of the following options regression, test or testlist for more info use --help")
            sys.exit()
        if not set(self.sim).issubset(["RTL","GL","GL_SDF"]):
            print (f"Fatal: {self.sim} isnt a correct type for -sim it should be one or combination of the following RTL, GL or GL_SDF")
            sys.exit()
    def set_tag(self):
        self.TAG = None # tag will be set in the main phase and other functions will use it
        if self.tag is not None:
            self.TAG = self.tag
        elif self.regression is not None: 
            self.TAG = f'{self.regression}_{datetime.now().strftime("%H_%M_%S_%d_%m")}'
        else: 
            self.TAG = f'run{random.randint(0,1000)}_{datetime.now().strftime("%H_%M_%S_%d_%m")}'
        Path(f"sim/{self.TAG}").mkdir(parents=True, exist_ok=True)
        print(f"Run tag: {self.TAG}")

    def def_env_vars(self):
        if os.getenv('CARAVEL_ROOT') is None or os.getenv('MCW_ROOT') is None:
            print(f"Fatal: CARAVEL_ROOT or MCW_ROOT are not defined")
            sys.exit()
        cocotb_path = f"{os.getenv('CARAVEL_ROOT')}/verilog/dv/cocotb"
        os.environ["CARAVEL_VERILOG_PATH"] = f"{os.getenv('CARAVEL_ROOT')}/verilog"
        os.environ["VERILOG_PATH"] = f"{os.getenv('MCW_ROOT')}/verilog"
        os.environ["CARAVEL_PATH"] = f"{os.getenv('CARAVEL_VERILOG_PATH')}"
        os.environ["FIRMWARE_PATH"] = f"{os.getenv('MCW_ROOT')}/verilog/dv/firmware"
        os.environ["RUNTAG"] = f"{self.TAG}"
        os.environ["ERRORMAX"] = f"{self.maxerr}"



import argparse
parser = argparse.ArgumentParser(description='Run cocotb tests')
parser.add_argument('-regression','-r', help='name of regression can found in tests.json')
parser.add_argument('-test','-t', nargs='+' ,help='name of test if no --sim provided RTL will be run <takes list as input>')
parser.add_argument('-sim', nargs='+' ,help='Simulation type to be run RTL,GL&GL_SDF provided only when run -test <takes list as input>')
parser.add_argument('-testlist','-tl', help='path of testlist to be run ')
parser.add_argument('-tag', help='provide tag of the run default would be regression name and if no regression is provided would be run_<random float>_<timestamp>_')
parser.add_argument('-maxerr', help='max number of errors for every test before simulation breaks default = 3')
parser.add_argument('-vcs','-v',action='store_true', help='use vcs as compiler if not used iverilog would be used')
parser.add_argument('-cov',action='store_true', help='enable code coverage')
parser.add_argument('-corner','-c', nargs='+' ,help='Corner type in case of GL_SDF run has to be provided')
parser.add_argument('-keep_pass_unzip',action='store_true', help='Normally the waves and logs of passed tests would be zipped. Using this option they wouldn\'t be zipped')
parser.add_argument('-caravan',action='store_true', help='simulate caravan instead of caravel')
args = parser.parse_args()
if (args.vcs) : 
    iverilog = False
    vcs = True
if args.cov: 
    coverage = True
if args.sim == None: 
    args.sim= ["RTL"]
if args.corner == None: 
    args.corner= ["nom-t"]
if args.keep_pass_unzip: 
    zip_waves = False
if args.caravan: 
    caravan = True
print(f"regression:{args.regression}, test:{args.test}, testlist:{args.testlist} sim: {args.sim}")
main(args)