493 lines
15 KiB
Python
493 lines
15 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
from cmath import nan
|
|
import json
|
|
import copy
|
|
import os
|
|
import sys
|
|
import subprocess
|
|
import shutil
|
|
import get_run_val as rval
|
|
from enum import Enum, auto
|
|
|
|
# used for HAPS or real chip
|
|
DRAM_BASE_SHIFT = 0
|
|
# DRAM_BASE_SHIFT = 0x60000000
|
|
|
|
class NoValue(Enum):
|
|
def __repr__(self):
|
|
return '<%s.%s>' % (self.__class__.__name__, self.name)
|
|
|
|
class CType(NoValue):
|
|
WAGNER = "730"
|
|
RAVEL = "1140"
|
|
|
|
"""
|
|
Versions for 730 and 1140.
|
|
"""
|
|
class VersionX30(NoValue):
|
|
SINGLE = "single"
|
|
MULTI = "multi"
|
|
MODEL = "model"
|
|
MODEL_OPT = "model_opt"
|
|
MODEL_REL = "model_rel"
|
|
MULTI_DBG = "multi_dbg"
|
|
MODEL_DBG = "model_dbg"
|
|
MODEL_DBG_EDA = "model_dbg_eda"
|
|
MODEL_ENC = "model_enc"
|
|
MODEL_CPU = "model_cpu"
|
|
|
|
def dbg_print_args():
|
|
bin = os.path.basename(sys.argv[0])
|
|
args = {" ".join(sys.argv[1:])}
|
|
print(f"=> [{bin}] arguments: {args}")
|
|
|
|
|
|
def load_json_file(path):
|
|
with open(path) as f:
|
|
return json.load(f)
|
|
|
|
def to_mebibyte(hex_value):
|
|
if hex_value is None:
|
|
return None
|
|
return int(hex_value, 16) / (2 ** 20)
|
|
|
|
def to_fw_addr(size):
|
|
return f"{hex(size)}00000"
|
|
|
|
def get_version_cfg(ctype, version):
|
|
bcfg = get_basic_cfg(ctype)
|
|
|
|
if ctype == CType.WAGNER.value:
|
|
config = get_730_version_cfg(bcfg, version)
|
|
if ctype == CType.RAVEL.value:
|
|
config = get_730_version_cfg(bcfg, version)
|
|
config["sram_size"] = 2048
|
|
config["weight_compress"] = True # always enabled
|
|
|
|
return config
|
|
|
|
def get_basic_cfg(ctype):
|
|
BASIC_CFG_730 = {
|
|
"def_data_bitw": 8,
|
|
"dram_base_addr": "0x80000000",
|
|
"dedicated_outbuf": True,
|
|
"sram_size": 1024,
|
|
"weight_compact": False,
|
|
"weight_compress": False,
|
|
"dynamic_fp": True,
|
|
"dynamic_json_path": "XXXXX.json",
|
|
"skip_backend": False,
|
|
"debug_mode": False,
|
|
"optimize": {
|
|
"cmd_size": False,
|
|
"do_loop_for_batch": False,
|
|
"dma": False,
|
|
"getw": False,
|
|
"pooling": False,
|
|
"parallel": False,
|
|
"tiling_for_dma": False,
|
|
},
|
|
"enable_ncore": True,
|
|
"use_ch_compact_fmt": True,
|
|
"fm_cut_not_support_as_cpu_node": True,
|
|
}
|
|
|
|
if ctype == CType.WAGNER.value:
|
|
return BASIC_CFG_730
|
|
elif ctype == CType.RAVEL.value:
|
|
return BASIC_CFG_730
|
|
else:
|
|
raise ValueError(f"invalid versionctype [{ctype}]")
|
|
|
|
def get_730_version_cfg(basic, version):
|
|
basic = update_run_val(basic)
|
|
|
|
SINGLE_CFG = merge_dict(basic, {
|
|
"single_layer_test": True,
|
|
"enable_ncore": False,
|
|
"use_ch_compact_fmt": False,
|
|
})
|
|
MULTI_CFG = merge_dict(basic, {
|
|
"optimize": {
|
|
"getw": True,
|
|
"pooling": True,
|
|
"parallel": True,
|
|
"tiling_for_dma": True,
|
|
}})
|
|
MODEL_CFG = merge_dict(basic, {
|
|
"optimize": {
|
|
"dma": True,
|
|
"getw": True,
|
|
"pooling": True,
|
|
"parallel": True,
|
|
"tiling_for_dma": True,
|
|
}
|
|
})
|
|
MULTI_DBG_CFG = merge_dict(MULTI_CFG, {
|
|
"debug_mode": True,
|
|
})
|
|
MODEL_DBG_CFG = merge_dict(MODEL_CFG, {
|
|
"debug_mode": True,
|
|
})
|
|
MODEL_DBG_EDA_CFG = merge_dict(MODEL_CFG, {"optimize": {
|
|
"getw": False,
|
|
}})
|
|
MODEL_OPT_CFG = merge_dict(MODEL_CFG, {"optimize": {
|
|
"cmd_size": True
|
|
}})
|
|
MODEL_REL_CFG = merge_dict(MODEL_OPT_CFG, {
|
|
})
|
|
MODEL_ENC_CFG = merge_dict(MODEL_CFG, {
|
|
"weight_compress": True,
|
|
"encryption_flag": True,
|
|
"encryption_key": "0x1234",
|
|
"encryption_file": "/home/rick/working/kdp720_compiler/resource/keys.bin",
|
|
})
|
|
|
|
if version == "single":
|
|
return SINGLE_CFG
|
|
elif version == "multi":
|
|
return MULTI_CFG
|
|
elif version == "model":
|
|
return MODEL_CFG
|
|
elif version == "model_opt" or version == "model_cpu":
|
|
return MODEL_OPT_CFG
|
|
elif version == "model_rel":
|
|
return MODEL_REL_CFG
|
|
elif version == "multi_dbg":
|
|
return MULTI_DBG_CFG
|
|
elif version == "model_dbg":
|
|
return MODEL_DBG_CFG
|
|
elif version == "model_dbg_eda":
|
|
return MODEL_DBG_EDA_CFG
|
|
elif version == "model_enc":
|
|
return MODEL_ENC_CFG
|
|
else:
|
|
raise ValueError(f"invalid version [{version}]")
|
|
|
|
def update_run_val(config):
|
|
run_val = rval.get_run_val()
|
|
|
|
return merge_dict(
|
|
config, {
|
|
"run_val": run_val,
|
|
})
|
|
|
|
|
|
def get_add_cfg(add_json):
|
|
cfg = {}
|
|
if add_json:
|
|
cfg = json.loads(add_json)
|
|
return cfg
|
|
|
|
|
|
def get_hack_cfg(hack_cfg):
|
|
cfg = {}
|
|
if hack_cfg:
|
|
cfg = load_json_file(hack_cfg)
|
|
return cfg
|
|
|
|
|
|
def get_radix_cfg(radix_cfg):
|
|
cfg = {}
|
|
|
|
if radix_cfg is None:
|
|
cfg["dynamic_fp"] = False
|
|
else:
|
|
if not os.path.isfile(radix_cfg):
|
|
raise FileNotFoundError(radix_cfg)
|
|
cfg = {
|
|
"dynamic_fp": True,
|
|
"dynamic_json_path": os.path.abspath(radix_cfg)
|
|
}
|
|
|
|
return cfg
|
|
|
|
def get_shape_cfg(shape_cfg):
|
|
cfg = {}
|
|
|
|
if shape_cfg is None:
|
|
return cfg
|
|
|
|
if not os.path.isfile(shape_cfg):
|
|
raise FileNotFoundError(shape_cfg)
|
|
cfg = {
|
|
"shape_info_cfg": os.path.abspath(shape_cfg)
|
|
}
|
|
|
|
return cfg
|
|
|
|
def delete_elements(data, elements_to_delete=[]):
|
|
for element in elements_to_delete:
|
|
if isinstance(element, list):
|
|
delete_elements(data, element)
|
|
continue
|
|
elif isinstance(element, dict):
|
|
for key, val in element.items():
|
|
delete_elements(data[key], [val])
|
|
continue
|
|
|
|
if element not in data:
|
|
continue
|
|
|
|
if isinstance(data, list):
|
|
data.remove(element)
|
|
elif isinstance(data, dict):
|
|
del data[element]
|
|
|
|
def merge_dict(base_dictionary, additional_dictionary, keys_to_delete=[]):
|
|
if additional_dictionary is None:
|
|
return base_dictionary
|
|
|
|
result = copy.deepcopy(base_dictionary)
|
|
|
|
delete_elements(result, keys_to_delete)
|
|
|
|
# merges additional_dictionary into result
|
|
for key in additional_dictionary:
|
|
if key not in result:
|
|
result[key] = additional_dictionary[key]
|
|
continue
|
|
|
|
if isinstance(result[key], dict) and isinstance(additional_dictionary[key], dict):
|
|
result[key] = merge_dict(result[key], additional_dictionary[key])
|
|
continue
|
|
|
|
result[key] = additional_dictionary[key]
|
|
|
|
return result
|
|
|
|
def env_set_cfg(cfg, ctype):
|
|
if os.getenv("RUN_IP_EVAL") == "1":
|
|
add_ip_eval_cfg(cfg, ctype)
|
|
|
|
if os.getenv("WT_COMPRESS") == "1":
|
|
cfg["weight_compress"] = True
|
|
|
|
return cfg
|
|
|
|
|
|
def add_ip_eval_cfg(cfg, ctype):
|
|
key = "ip_evaluator_cfg"
|
|
if key not in cfg:
|
|
cfg[key] = get_ip_eval_cfg(ctype)
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
def remove_ip_eval_cfg(cfg):
|
|
key = "ip_evaluator_cfg"
|
|
if key in cfg:
|
|
del cfg[key]
|
|
|
|
|
|
def get_ip_eval_cfg(ctype):
|
|
fpath = f"ip_eval/ip_eval_{ctype}.json"
|
|
return get_resource_fpath(fpath)
|
|
|
|
|
|
def get_resource_fpath(rel_path):
|
|
# /home/kai/compiler/test/gen_rtl_test/utils/gen_config.py
|
|
script_path = os.path.realpath(__file__)
|
|
proj_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(script_path))))
|
|
return os.path.join(proj_dir, "resource", rel_path)
|
|
|
|
|
|
def hack_dram_addr(cfg):
|
|
if DRAM_BASE_SHIFT <= 0:
|
|
return cfg
|
|
|
|
print(f"Hack dram addr config by adding \"0x{DRAM_BASE_SHIFT:x}\":", file=sys.stderr)
|
|
for key, val in cfg.items():
|
|
if key[-5:] == "_addr":
|
|
addr = DRAM_BASE_SHIFT + int(val, 16)
|
|
addr = str(hex(addr))
|
|
print(f" {key}: {val} => {addr}", file=sys.stderr)
|
|
print("", file=sys.stderr)
|
|
|
|
return cfg
|
|
|
|
|
|
def save_config(path, cfg):
|
|
with open(path, "w") as fp:
|
|
json.dump(cfg, fp, indent=4, sort_keys=True)
|
|
fp.write('\n')
|
|
|
|
# use opt_compile.py to find best compiler config
|
|
def gen_best_config(ctype, opt_model, image_cut_search_args_str, cfg, cfg_path):
|
|
def get_script_path():
|
|
opt_compile_dir = os.getenv("OPT_COMPILE_DIR")
|
|
if opt_compile_dir and os.path.exists(opt_compile_dir):
|
|
opt_compile = os.path.join(opt_compile_dir, 'opt_compile.py')
|
|
else:
|
|
# get opt_compile.py in default path. ex: compiler/test/gen_rtl_test/utils/gen_config.py
|
|
script_path = os.path.realpath(__file__)
|
|
proj_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(script_path))))
|
|
opt_compile = os.path.join(proj_dir, 'bin/opt_compile/opt_compile.py')
|
|
|
|
assert os.path.exists(
|
|
opt_compile
|
|
), f"environment variable OPT_COMPILE_DIR [{opt_compile_dir}] doesn't exist ! please export OPT_COMPILE_DIR=XXXX"
|
|
return opt_compile
|
|
|
|
if not opt_model:
|
|
return 0
|
|
|
|
print('=> [opt_compile]')
|
|
# env [COMPILER_BIN_DIR] is used and checked in opt_compile.py
|
|
assert os.path.exists(opt_model), f"opt_model [{opt_model}] doesn't exist"
|
|
|
|
if ctype not in ['730', '1140']:
|
|
print('[Not support] only support for 730|1140 !')
|
|
return -1
|
|
|
|
# create tmp_cfg, and add ip_evaluator_cfg key
|
|
out_dir = os.path.dirname(os.path.realpath(cfg_path))
|
|
tmp_cfg = copy.deepcopy(cfg)
|
|
is_add_ip_eval = add_ip_eval_cfg(tmp_cfg, ctype)
|
|
tmp_cfg_path = os.path.join(out_dir, 'tmp_cfg.json')
|
|
save_config(tmp_cfg_path, tmp_cfg)
|
|
|
|
# run opt_compile.py, and will do:
|
|
# - create opt_compile. directory to store debug files. (will be removed after run)
|
|
# - create opt_output. directory to store exported files. (wll keep after run)
|
|
# - export opt.log
|
|
# - export best_config.json (overwrite to cfg_path)
|
|
# - sub-module depandency:
|
|
# - image_cut_search: export best_image_cut_config.json
|
|
script = get_script_path()
|
|
opt_log_dir = os.path.join(out_dir, 'opt_compile')
|
|
opt_out_dir = os.path.join(out_dir, 'opt_output')
|
|
|
|
basic_args_str = f"{script} {ctype} {opt_model} {tmp_cfg_path} {opt_log_dir} {opt_out_dir}"
|
|
search_args_str = f'--image_cut_search_args "{image_cut_search_args_str}"'
|
|
basic_args = basic_args_str.split()
|
|
search_args =['--image_cut_search_args', image_cut_search_args_str]
|
|
|
|
print(f'{basic_args_str} {search_args_str}');
|
|
cmd_args = basic_args + search_args
|
|
o = subprocess.run(cmd_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
# export opt.log
|
|
opt_log = f"{opt_log_dir}/opt.log"
|
|
if os.path.exists(opt_log):
|
|
shutil.copy(opt_log, out_dir, follow_symlinks=True)
|
|
|
|
if o.returncode != 0:
|
|
print('\n[Failed] failed to run opt_compile:')
|
|
if o.stdout:
|
|
print(o.stdout.decode())
|
|
|
|
if o.stderr:
|
|
print(o.stderr.decode())
|
|
|
|
shutil.rmtree(opt_log_dir)
|
|
sys.exit(o.returncode)
|
|
|
|
best_cfg_path = os.path.join(opt_log_dir, 'best_config.json')
|
|
assert os.path.exists(best_cfg_path), f"best_config.json doesn't exist"
|
|
best_cfg = load_json_file(best_cfg_path)
|
|
#export best_image_cut_config.json for image_cut_search
|
|
if 'fmap_cut' in best_cfg and best_cfg['fmap_cut']['mode'] == 'manual':
|
|
org_fmap_cut_cfg = best_cfg['fmap_cut']['cfg']
|
|
new_fmap_cut_cfg = os.path.join(out_dir, 'best_image_cut_config.json')
|
|
shutil.copyfile(org_fmap_cut_cfg, new_fmap_cut_cfg)
|
|
best_cfg['fmap_cut']['cfg'] = new_fmap_cut_cfg
|
|
if is_add_ip_eval:
|
|
remove_ip_eval_cfg(best_cfg)
|
|
# export best_config.json (overwrite to cfg_path)
|
|
save_config(cfg_path, best_cfg)
|
|
|
|
shutil.rmtree(opt_log_dir)
|
|
return 0
|
|
|
|
def gen_all():
|
|
versions = {
|
|
CType.WAGNER : VersionX30,
|
|
}
|
|
for ctype in versions:
|
|
output_directory = f"./{ctype.value}"
|
|
if not os.path.exists(output_directory):
|
|
os.makedirs(output_directory, exist_ok=True)
|
|
for version in versions[ctype]:
|
|
config = get_version_cfg(ctype.value, version.value)
|
|
file_name = f"config_{ctype.value}_{version.value}.json"
|
|
config_path = f"{output_directory}/{file_name}"
|
|
save_config(config_path, config)
|
|
|
|
################
|
|
## MAIN ##
|
|
################
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description="generate config.json for compiler")
|
|
ctypes = "|".join([ctype.value for ctype in CType])
|
|
parser.add_argument("-t", "--ctype", help=f"compiler type [{ctypes}]")
|
|
parser.add_argument(
|
|
"-v",
|
|
"--version",
|
|
help="config version: [single|multi|model|model_rel|model_opt|model_dbg|model_dbg_eda]")
|
|
parser.add_argument("-f", "--tmpl_cfg", help="path to template config (if exists, version is skipped)")
|
|
parser.add_argument("-o", "--output", default="config.json", help="path to output config")
|
|
parser.add_argument("-r", "--radix_cfg", help="path to radix config")
|
|
parser.add_argument("-s", "--shape_cfg", help="path to shape info config")
|
|
parser.add_argument("-k", "--hack_cfg", help="path to hack config")
|
|
parser.add_argument("-a",
|
|
"--add_json",
|
|
action='append',
|
|
help="additional config in json str format, e.g. '{\"single_layer_test\":true,\"hstride\":true}'")
|
|
parser.add_argument(
|
|
"-m",
|
|
"--opt_model",
|
|
help=
|
|
"model path. run opt_compile to find best compiler config. please export COMPILER_BIN_DIR=XXXX to specify compiler bin dir, and export OPT_COMPILE_DIR=XXXX to specify opt compile bin dir."
|
|
)
|
|
parser.add_argument("--image_cut_search_args", type=str, default=" -r -u -t -s",
|
|
help="arguments for image cut search. only valid when enable [-m|--opt_model]. e.g: this value can be [-r|-u|-t|-s|-pgs]. defult is ' -r -u -t -s'. hint: must add space before each argument.\n"
|
|
"see bin/opt_compile/modules/image_cut_search/all_rule_auto_release.py -h for more detail.")
|
|
parser.add_argument("-g", "--gen-all-version", action="store_true", help="Generate basic configs for all version with all ctype to verify or to debug.")
|
|
|
|
# dbg_print_args()
|
|
|
|
args = parser.parse_args()
|
|
if args.gen_all_version:
|
|
gen_all()
|
|
print("Generation done.")
|
|
sys.exit()
|
|
|
|
ctype = args.ctype
|
|
|
|
tmpl_cfg = args.tmpl_cfg
|
|
if tmpl_cfg:
|
|
cfg = load_json_file(tmpl_cfg)
|
|
else:
|
|
cfg = get_version_cfg(ctype, args.version)
|
|
|
|
hack_cfg = get_hack_cfg(args.hack_cfg)
|
|
cfg = merge_dict(cfg, hack_cfg)
|
|
|
|
radix_cfg = get_radix_cfg(args.radix_cfg)
|
|
cfg = merge_dict(cfg, radix_cfg)
|
|
|
|
shape_cfg = get_shape_cfg(args.shape_cfg)
|
|
cfg = merge_dict(cfg, shape_cfg)
|
|
|
|
if args.add_json:
|
|
for js in args.add_json:
|
|
add_cfg = get_add_cfg(js)
|
|
cfg = merge_dict(cfg, add_cfg)
|
|
|
|
cfg = env_set_cfg(cfg, ctype)
|
|
cfg = hack_dram_addr(cfg)
|
|
|
|
# save to file
|
|
cfg_path = args.output
|
|
save_config(cfg_path, cfg)
|
|
|
|
# find best compiler config and replace the old one
|
|
if 0 != gen_best_config(ctype, args.opt_model, args.image_cut_search_args, cfg, cfg_path):
|
|
sys.exit(-1)
|