Exemplo n.º 1
0
def print_to_results(gate_config_file, required_gates, results_file, prefix):
    gates = read_gate_config(gate_config_file, None)

    generate_all = required_gates is None or required_gates == "" or "ALL" in required_gates

    t_p_list = []
    t_p_percent_list = []
    t_p_mode_list = []
    name_list = []
    channel_type_list = []
    channel_location_list = []
    n_up_list = []
    n_do_list = []
    x_1_up_list = []
    x_1_do_list = []
    tau1_up_list = []
    tau1_do_list = []
    tau2_up_list = []
    tau2_do_list = []

    for name in sorted(gates.keys()):
        gate = gates[name]
        if not generate_all and name not in required_gates:
            my_print("Ignoring: " + name)
            continue  # we do not want to generate all gates, if not necessary for the circuit

        t_p_list.append(gate.T_P)
        t_p_percent_list.append(gate.T_P_percent)
        t_p_mode_list.append(gate.T_P_mode)
        name_list.append(gate.entity_name)
        channel_type_list.append(gate.channel_type)
        channel_location_list.append(gate.channel_location)
        append_channel_parameter(n_up_list, gate, "N_UP")
        append_channel_parameter(n_do_list, gate, "N_DO")
        append_channel_parameter(x_1_up_list, gate, "X_1_UP")
        append_channel_parameter(x_1_do_list, gate, "X_1_DO")
        append_channel_parameter(tau1_up_list, gate, "TAU_1_UP")
        append_channel_parameter(tau1_do_list, gate, "TAU_1_DO")
        append_channel_parameter(tau2_up_list, gate, "TAU_2_UP")
        append_channel_parameter(tau2_do_list, gate, "TAU_2_DO")

    # Now extend the results dictionary
    results = dict()
    append_to_result_dict(results, prefix + 'T_P', t_p_list)
    append_to_result_dict(results, prefix + 'T_P_Percent', t_p_percent_list)
    append_to_result_dict(results, prefix + 'T_P_Mode', t_p_mode_list)
    append_to_result_dict(results, prefix + 'name', name_list)
    append_to_result_dict(results, prefix + 'channel_type', channel_type_list)
    append_to_result_dict(results, prefix + 'channel_location',
                          channel_location_list)
    append_to_result_dict(results, prefix + 'n_up', n_up_list)
    append_to_result_dict(results, prefix + 'n_do', n_do_list)
    append_to_result_dict(results, prefix + 'x_1_up', x_1_up_list)
    append_to_result_dict(results, prefix + 'x_1_do', x_1_do_list)
    append_to_result_dict(results, prefix + 'tau_1_up', tau1_up_list)
    append_to_result_dict(results, prefix + 'tau_1_do', tau1_do_list)
    append_to_result_dict(results, prefix + 'tau_2_up', tau2_up_list)
    append_to_result_dict(results, prefix + 'tau_2_do', tau2_do_list)

    extend_results(results_file, results)
Exemplo n.º 2
0
def combine_gates(default_config_file, circuit_config_file,
                  target_config_file):
    gates = read_gate_config(default_config_file, circuit_config_file)

    with open(target_config_file, "w") as gate_cfg_file:
        gate_cfg_file.write(
            json.dumps(gates, cls=ObjectEncoder, indent=2, sort_keys=True))
Exemplo n.º 3
0
def prepate_gates(default_config_file, circuit_config_file,
                  template_gate_config_file, output_file, required_gates):
    gates = read_gate_config(default_config_file, circuit_config_file)

    gate_config_template = ""
    with open(template_gate_config_file, 'r') as tempfile:
        gate_config_template = tempfile.read()

    generate_all = required_gates is None or "ALL" in required_gates
    file_content = ""
    # now that we have all the gates we want to create --> create them
    for name, gate in gates.items():
        if not generate_all and name not in required_gates:
            my_print("Ignoring: " + name)
            continue  # we do not want to generate all gates, if not necessary for the circuit

        channel_parameters = ""
        for param_key, param_value in gate.channel_parameters.items():
            channel_parameters += replace_special_chars(
                str(param_key)) + ": " + replace_special_chars(
                    str(param_value)) + "\\\\"

        if channel_parameters != "":
            channel_parameters = "Additional channel parameters: \\\\\n" + channel_parameters

        file_content = gate_config_template.replace(
            "%##ENTITY_NAME##%",
            replace_special_chars(gate.entity_name)).replace(
                "%##CHANNEL_TYPE##%",
                replace_special_chars(str(gate.channel_type))).replace(
                    "%##EXP_CHANNEL_LOCATION##%",
                    replace_special_chars(str(gate.channel_location))).replace(
                        "%##CHANNEL_LOCATION##%",
                        str(gate.channel_location)).replace(
                            "%##T_P##%", str(gate.T_P)).replace(
                                "%##FUNCTION##%", gate.function).replace(
                                    "%##INPUTS##%",
                                    ", ".join(gate.inputs)).replace(
                                        "%##OUTPUTS##%",
                                        ", ".join(gate.outputs)).replace(
                                            "%##CHANNEL_PARAMETERS##%",
                                            channel_parameters)

    with open(output_file, 'w') as outfile:
        outfile.write(file_content)
Exemplo n.º 4
0
def generate_gates(default_config_file, circuit_config_file, gate_dir,
                   gate_template_file, gate_input_process_template_file,
                   use_gidm, structure_file, tt_file_path,
                   generate_gate_per_instance, vectors_dir_file_path,
                   required_gates):

    gates = read_gate_config(default_config_file, circuit_config_file)
    use_gidm = to_bool(use_gidm)
    generate_gate_per_instance = to_bool(generate_gate_per_instance)

    # read template for gate
    gate_template = ""
    with open(gate_template_file, 'r') as template_file:
        gate_template = template_file.read()

    # read template for gate input process
    gate_input_process_template = ""
    with open(gate_input_process_template_file, 'r') as template_file:
        gate_input_process_template = template_file.read()

    generate_all = required_gates is None or "ALL" in required_gates

    structure = CircuitStructure()
    if use_gidm or generate_gate_per_instance:
        # now we need to read the structure.json file and generate
        structure = read_circuit_structure(structure_file)
    else:
        # This case is obsolete, we do not want to maintain this option any more.
        # Simply switch to GIDM or to generate_gate_per_instance
        # There is no function reduction, only a little performance sacrifice
        my_print("Gate generation configuration not supported any more!",
                 EscCodes.FAIL)

    # now that we have all the gates we want to create --> create them
    for name, gate in gates.items():
        if not check_gate_creation(generate_all, name, required_gates, gate):
            continue

        output = gate.outputs[0]

        function, function_input_list, delay_channel_list, signal_list = build_function(
            use_gidm, gate, output)

        entity_generic = build_entity_generic(gate)

        channel, d_up, d_down = build_channel_parameters(
            delay_channel_list, gate, output, use_gidm)

        arch_name = build_arch_name(use_gidm, gate)

        # Replace stuff that is independent of GIDM / IDM, and the ImplementationType
        content = gate_template.replace("##ARCH_NAME##", arch_name).replace(
            "##ARCH_FUNCTION##",
            function).replace("##ENTITY_GENERIC##", entity_generic).replace(
                "##GATE_SPECIFIC_PARAMETERS##", "")

        if use_gidm:
            generate_gate_gidm(gate, content, structure, name, channel,
                               gate_dir, gate_input_process_template,
                               tt_file_path, function_input_list, output,
                               vectors_dir_file_path)
        else:
            generate_gate_idm(gate, content, structure, name, channel,
                              gate_dir, d_up, d_down, signal_list,
                              generate_all, required_gates)
Exemplo n.º 5
0
def prepare_testbench(circuit_file_in, circuit_file_out, process_template_file,
                      input_names, vector_names, default_gate_config_file,
                      circuit_gate_config_file, circuit_configuration_file):
    my_print("prepare_testbench")

    gates = read_gate_config(default_gate_config_file,
                             circuit_gate_config_file)

    circuit_content = ""
    with open(circuit_file_in, 'r') as tempfile:
        circuit_content = tempfile.read()

    process_template = ""
    with open(process_template_file, 'r') as tempfile:
        process_template = tempfile.read()

    circuit_configuration = ""
    if circuit_configuration_file and os.path.isfile(
            circuit_configuration_file):
        with open(circuit_configuration_file, 'r') as tempfile:
            circuit_configuration = tempfile.read()

    input_list = input_names.split(" ")
    vector_list = vector_names.split(" ")

    input_list = [x.strip(" \r\t\n") for x in input_list if x.strip(" \r\n\t")]
    vector_list = [
        x.strip(" \r\t\n") for x in vector_list if x.strip(" \r\n\t")
    ]

    if len(input_list) != len(vector_list):
        my_print(
            "input_names and vector_names have a different length, this should not happen!",
            EscCode.FAIL)
        return

    input_process_content = ""
    for x in range(0, len(input_list)):
        my_print("Signal: " + input_list[x])
        input_process_content += process_template.replace(
            "##SIGNALNAME##", input_list[x]).replace("##VECTORNAME##",
                                                     vector_list[x])
        input_process_content += "\n\n\n"

    for name, gate in gates.items():
        additional_generics = ""
        for key, value in gate.channel_parameters.items():
            additional_generics = ",\n\t\t\t" + str(key) + " => " + str(value)
        circuit_configuration = circuit_configuration.replace(
            "##GATE_ARCHITECTURE_" + name + "##",
            str(gate.channel_type) + "_" + str(gate.channel_location)).replace(
                "##T_P_" + name + "##",
                str(gate.T_P) + " ps").replace(
                    "##CHANNEL_SPECIFIC_GENERICS_" + name + "##",
                    additional_generics)

    input_process_content = """
		init_proc : PROCESS
		BEGIN
			init_wrapper;
			initialized <= '1';
			WAIT;
		END PROCESS;


		""".format() + input_process_content

    all_inputs_done = " AND ".join(
        ["{input}_done = '1'".format(input=input) for input in input_list])
    input_process_content = input_process_content + """


		clean_process : PROCESS
		BEGIN
			WAIT UNTIL {all_done};
			WAIT for 10 sec; -- This is to ensure that clean_wrapper is definitely called after all transitions have been handled
			clean_wrapper;
			WAIT;
		END PROCESS;
		""".format(all_done=all_inputs_done)

    content_to_write = circuit_content.replace("##INPUT_PROCESS##",
                                               input_process_content).replace(
                                                   "##CIRCUIT_CONFIGURATION##",
                                                   circuit_configuration)

    with open(circuit_file_out, 'w') as outfile:
        outfile.write(content_to_write)
def generate_cell_initialization(circuit_folder, characterization_conf_file_path, structure_file_path, default_gate_config_file_path, circuit_gate_config_file_path, start_value):
    characterization_conf_file_path =  os.path.join(circuit_folder, characterization_conf_file_path)
    structure_file_path =  os.path.join(circuit_folder, structure_file_path)
    default_gate_config_file_path =  os.path.join(circuit_folder, default_gate_config_file_path)
    circuit_gate_config_file_path =  os.path.join(circuit_folder, circuit_gate_config_file_path)
    start_value = str(start_value)

    init_mapping = dict()

    # Need to read the characterization file
    char_conf = None
    with open(characterization_conf_file_path) as json_file:
        char_conf = json.load(json_file)

    # Need to read structure file
    structure = read_circuit_structure(structure_file_path)

    # Need to read the gate config file, to check which gate type we have
    gate_config = read_gate_config(default_gate_config_file_path, circuit_gate_config_file_path)

    # Traverse over the dependeny tree and fill out init mapping
    importer = DictImporter()
    dependency_tree = importer.import_(char_conf['dependency_tree'])
    for node in PreOrderIter(dependency_tree):	        
        if node.name == 'Top':
            continue

        cellname = get_cellname(char_conf, node.name)

        # Now use the cellname to find the cell_type in the structure.json file

        found_cell = find_cell_in_structure(structure, cellname)
        assert(found_cell)

        # Now we use the cell_type and find the function in the gate config
        gate = gate_config[found_cell.cell_type]
        # print(node.name, cellname, found_cell, gate, gate.function)

        assert(gate.function == "not" or gate.function == "")
        input_found_cell = gate.inputs
        assert(len(input_found_cell) == 1)
        input_found_cell = input_found_cell[0]

        # We need to find the predecessor and get the output value of the predecessor
        pred = find_pred_interconnect(structure, found_cell, input_found_cell)
        print(pred, cell)

        # Get the value of the predecessor from the dict
        in_value = None
        if pred.from_instance in init_mapping:
            in_value = init_mapping[pred.from_instance]
        else:
            in_value = start_value

        in_value = to_bool(in_value)
        logic_function = None
        if gate.function == "not":
            logic_function = my_not
        elif gate.funtion == "":
            logic_function = my_id
        else:
            assert(False)

        init_mapping[cell.instance] = bool_to_logic(logic_function(in_value))       
    

    # Write the init mapping to the structure file and save it
    structure.init = init_mapping
    save_circuit_structure(structure_file_path, structure)
Exemplo n.º 7
0
def multi_exec_sim(config_file):
    temp_folder_name = 'temp'
    if not os.path.exists(temp_folder_name):
        os.makedirs(temp_folder_name)

    waveform_file_name = 'generate.json'
    waveform_generation = read_generate_cfg(
        os.path.join(os.environ["WAVEFORM_GENERATION_CONFIG_DIR"],
                     waveform_file_name))
    with open(os.path.join(temp_folder_name, waveform_file_name),
              "w") as waveform_cfg_file:
        waveform_cfg_file.write(
            json.dumps(waveform_generation,
                       cls=ObjectEncoder,
                       indent=2,
                       sort_keys=True))
    os.environ["WAVEFORM_GENERATION_CONFIG_DIR"] = os.path.join(
        os.getcwd(), temp_folder_name)

    # Read the multi_exec config file
    multi_exec = MultiExec()
    with open(config_file, "r") as cfg_file:
        jsonobject = json.load(cfg_file)
        for key, value in jsonobject.items():
            if key.lower() == "waveform_generation":
                for elem in value:
                    cfg = copy.deepcopy(
                        waveform_generation
                    )  # Required, because we want the default values from the default config file
                    cfg.__dict__.update(elem)
                    multi_exec.waveform_generation.append(cfg)
            elif key.lower() == "gate_generation":
                multi_exec.gate_generation.__dict__.update(value)
            else:
                multi_exec.__dict__[key] = value

    if get_print_level() > PrintLevel.INFORMATION:
        os.environ["MAKEFILE_PRINT_INFO"] = "False"

    # innermost loops (keep waveform!) must have lower numbers than than properties where a new waveform has to be created
    KEY_WAVEFORM = 8
    KEY_SPICE_PROPERTIES = 7  # we can keep the waveform, but need to re-execute the Spice Simulation for these properties
    KEY_CROSSING_PROPERTIES = 6
    KEY_DIGSIM_PROPERTIES = 5  # no need to re-execute SPICE, but we need to re-run our digital (ModelSim) simulation. ==> For example if only the sdf / spef File is changed
    KEY_INVOLUTION_PROPERTIES = 4  # we need to reexectue make sim_involution, since for example the SDF has changed (only allowed if we use a different SDF file for (G)IDM and Standard Delay Model, or if USE_GIDM flag is changed)
    KEY_CHANNEL_LOCATION = 3
    KEY_CHANNEL_TYPE = 2
    KEY_T_P = 1

    # Prepare a dictionary with the properties to change and a index to the current element
    property_dict = dict()
    total_sim_num = 1
    if len(multi_exec.waveform_generation) > 0:
        property_dict[KEY_WAVEFORM] = 0
        total_sim_num = total_sim_num * len(multi_exec.waveform_generation)
    if len(multi_exec.gate_generation.t_p_list) > 0:
        property_dict[KEY_T_P] = 0
        total_sim_num = total_sim_num * len(
            multi_exec.gate_generation.t_p_list)
    if len(multi_exec.gate_generation.channel_location_list) > 0:
        property_dict[KEY_CHANNEL_LOCATION] = 0
        total_sim_num = total_sim_num * len(
            multi_exec.gate_generation.channel_location_list)
    if len(multi_exec.gate_generation.channel_type_list) > 0:
        property_dict[KEY_CHANNEL_TYPE] = 0
        total_sim_num = total_sim_num * len(
            multi_exec.gate_generation.channel_type_list)
    if len(multi_exec.spice_properties) > 0:
        property_dict[KEY_SPICE_PROPERTIES] = 0
        total_sim_num = total_sim_num * len(multi_exec.spice_properties)
    if len(multi_exec.crossing_properties) > 0:
        property_dict[KEY_CROSSING_PROPERTIES] = 0
        total_sim_num = total_sim_num * len(multi_exec.crossing_properties)
    if len(multi_exec.digsim_properties) > 0:
        property_dict[KEY_DIGSIM_PROPERTIES] = 0
        total_sim_num = total_sim_num * len(multi_exec.digsim_properties)
    if len(multi_exec.involution_properties) > 0:
        property_dict[KEY_INVOLUTION_PROPERTIES] = 0
        total_sim_num = total_sim_num * len(multi_exec.involution_properties)

    last_key_to_keep_waveform = KEY_SPICE_PROPERTIES
    last_key_to_keep_crossings = KEY_DIGSIM_PROPERTIES
    last_key_to_keep_spice = KEY_CROSSING_PROPERTIES
    last_key_to_keep_stddigsim = KEY_INVOLUTION_PROPERTIES

    last_key_to_keep_group_count = KEY_T_P
    last_key_to_keep_reference_count = KEY_SPICE_PROPERTIES

    length_dict = dict()
    length_dict[KEY_WAVEFORM] = len(multi_exec.waveform_generation)
    length_dict[KEY_SPICE_PROPERTIES] = len(multi_exec.spice_properties)
    length_dict[KEY_CROSSING_PROPERTIES] = len(multi_exec.crossing_properties)
    length_dict[KEY_DIGSIM_PROPERTIES] = len(multi_exec.digsim_properties)
    length_dict[KEY_INVOLUTION_PROPERTIES] = len(
        multi_exec.involution_properties)
    length_dict[KEY_CHANNEL_LOCATION] = len(
        multi_exec.gate_generation.channel_location_list)
    length_dict[KEY_CHANNEL_TYPE] = len(
        multi_exec.gate_generation.channel_type_list)
    length_dict[KEY_T_P] = len(multi_exec.gate_generation.t_p_list)

    # 4. simulate
    my_print("Keep waveform: " + str(multi_exec.keep_waveform),
             EscCodes.OKBLUE, override_print_env_flag)
    # If keep_waveform is false, we just re-execute the complete toolchain fpr each configuration (do not keep SPICE, STD DIGISIM if possible)
    for iteraterion in range(multi_exec.N):
        my_print("Iteration: " + str(iteraterion + 1), EscCodes.OKBLUE,
                 override_print_env_flag)
        keep_waveform_local = False  # we ALWAYS want a new waveform in a new iteration
        keep_std_digsim = False
        keep_spice = False
        keep_crossings = False

        curr_sim_num = 1
        config_num = 0

        # Reset ME_reference_group and ME_group, these are "counters" which are later used for reporting
        os.environ[
            "ME_reference_group"] = "0"  # The ME_reference_group is incremented when the waveform config changes
        os.environ[
            "ME_group"] = "0"  # The ME_group is incremented when a channel property changes
        prev_param_mode = None
        me_group_param_mode_dict = dict()

        # reset property_dict indices (value is a pointer to the current setting for each key)
        for key in property_dict.keys():
            property_dict[key] = 0

        while True:
            if not keep_waveform_local:
                my_print("New waveform should be generated!", EscCodes.OKBLUE,
                         override_print_env_flag)
                waveform_file = "multi_exec_" + time.strftime(
                    "%Y%m%d_%H%M%S") + ".json"
                os.environ["INPUT_WAVEFORM"] = waveform_file

            my_print(
                "Iteration: " + str(iteraterion + 1) + " / " +
                str(multi_exec.N) + ", Simulation: " + str(curr_sim_num) +
                " / " + str(total_sim_num), EscCodes.OKBLUE,
                override_print_env_flag)

            # set the current spice_properties (if we have some). Note that these properties need to be checked with ifndef PROP_NAME before they are exported in the default *.cfg files
            set_config_property_list(multi_exec.spice_properties,
                                     property_dict, KEY_SPICE_PROPERTIES)
            set_config_property_list(multi_exec.crossing_properties,
                                     property_dict, KEY_CROSSING_PROPERTIES)
            set_config_property_list(multi_exec.digsim_properties,
                                     property_dict, KEY_DIGSIM_PROPERTIES)
            set_config_property_list(multi_exec.involution_properties,
                                     property_dict, KEY_INVOLUTION_PROPERTIES)

            # "Copy" the config files which will be adapted to the temp folder
            gate_config_name = 'gate_config.json'
            gate_default_config_file = os.environ["GENERAL_GATE_CONFIG"]
            gate_circuit_config_file = os.environ["CIRCUIT_GATE_CONFIG"]

            gates = read_gate_config(gate_default_config_file,
                                     gate_circuit_config_file)

            with open(os.path.join(temp_folder_name, gate_config_name),
                      "w") as gate_cfg_file:
                gate_cfg_file.write(
                    json.dumps(gates,
                               cls=ObjectEncoder,
                               indent=2,
                               sort_keys=True))

            # update the environment variables to the new generated config files (valid for this process and all child process)
            os.environ["GENERAL_GATE_CONFIG"] = os.path.join(
                os.getcwd(), temp_folder_name, gate_config_name)
            os.environ["CIRCUIT_GATE_CONFIG"] = os.path.join(
                os.getcwd(), temp_folder_name, gate_config_name)

            # Deepcopy required if we set one property in the previous simulation, but not in th current one
            # --> we want to have the value of the default config file
            new_gates = copy.deepcopy(gates)
            new_waveform_generation = copy.deepcopy(waveform_generation)
            config_num = config_num + 1
            # iterate over all properties to set
            for key, value in property_dict.items():
                if key == KEY_WAVEFORM:
                    new_waveform_generation = multi_exec.waveform_generation[
                        value]
                elif key == KEY_T_P:
                    for gate in new_gates.values():
                        # Make distinction between ABSOLUTE and PERCENT
                        if isinstance(
                                multi_exec.gate_generation.t_p_list[value],
                                list):
                            mode = multi_exec.gate_generation.t_p_list[value][
                                1]
                            gate.T_P_mode = mode
                            if mode == ParameterMode.ABSOLUTE:
                                gate.T_P = multi_exec.gate_generation.t_p_list[
                                    value][0]
                            elif mode == ParameterMode.PERCENT:
                                gate.T_P_percent = multi_exec.gate_generation.t_p_list[
                                    value][0]
                            else:
                                my_print("Error: Unknown T_P_mode:  " + mode,
                                         EscCodes.FAIL,
                                         override_print_env_flag)
                        else:
                            # Default behaviour (ABSOLUTE)
                            gate.T_P = multi_exec.gate_generation.t_p_list[
                                value]
                            gate.T_P_mode = ParameterMode.ABSOLUTE

                        # Handle ME_group counter (must be different between ABSOLUTE and percent
                        if prev_param_mode is None:
                            prev_param_mode = gate.T_P_mode
                            me_group_param_mode_dict[prev_param_mode] = int(
                                os.environ["ME_group"])
                        elif prev_param_mode != gate.T_P_mode:
                            # check the dict if we already have a group id for this T_P
                            if gate.T_P_mode in me_group_param_mode_dict:
                                # go back to the already used group id
                                os.environ["ME_group"] = str(
                                    me_group_param_mode_dict[gate.T_P_mode])
                            else:
                                # Create new group id
                                me_group_param_mode_dict[gate.T_P_mode] = max(
                                    me_group_param_mode_dict.values()) + 1
                                # and use this id
                                os.environ["ME_group"] = str(
                                    me_group_param_mode_dict[gate.T_P_mode])
                            prev_param_mode = gate.T_P_mode

                elif key == KEY_CHANNEL_LOCATION:
                    for gate in new_gates.values():
                        gate.channel_location = multi_exec.gate_generation.channel_location_list[
                            value]
                elif key == KEY_CHANNEL_TYPE:
                    gate_config_dict = multi_exec.gate_generation.channel_type_list[
                        value]
                    for gate_key, gate_value in new_gates.items():
                        gate_key_for_copy = "ALL"  # use the default configuration for this gate
                        if gate_key in gate_config_dict:
                            gate_key_for_copy = gate_key  # we found a more specific configuration for this gate => use it
                        if "channel_type" in gate_config_dict[
                                gate_key_for_copy].keys():
                            gate_value.channel_type = gate_config_dict[
                                gate_key_for_copy]["channel_type"]
                        if "channel_parameters" in gate_config_dict[
                                gate_key_for_copy].keys():
                            gate_value.channel_parameters = gate_config_dict[
                                gate_key_for_copy]["channel_parameters"]
                elif key == KEY_SPICE_PROPERTIES:
                    # nothing to do?
                    pass
                elif key == KEY_CROSSING_PROPERTIES:
                    # nothing to do?
                    pass
                elif key == KEY_DIGSIM_PROPERTIES:
                    # nothing to do?
                    pass
                elif key == KEY_INVOLUTION_PROPERTIES:
                    # nothing to do?
                    pass
                else:
                    my_print("Error: Undefined key", EscCodes.FAIL,
                             override_print_env_flag)

            # write the files after setting all the properties
            with open(os.path.join(temp_folder_name, gate_config_name),
                      "w") as gate_cfg_file:
                gate_cfg_file.write(
                    json.dumps(new_gates,
                               cls=ObjectEncoder,
                               indent=2,
                               sort_keys=True))

            with open(os.path.join(temp_folder_name, waveform_file_name),
                      "w") as waveform_cfg_file:
                waveform_cfg_file.write(
                    json.dumps(new_waveform_generation,
                               cls=ObjectEncoder,
                               indent=2,
                               sort_keys=True))

            # TODO: Remove, just for debugging reasons:
            with open(
                    os.path.join(temp_folder_name,
                                 gate_config_name + str(config_num)),
                    "w") as gate_cfg_file:
                gate_cfg_file.write(
                    json.dumps(new_gates,
                               cls=ObjectEncoder,
                               indent=2,
                               sort_keys=True))
            with open(
                    os.path.join(temp_folder_name,
                                 waveform_file_name + str(config_num)),
                    "w") as waveform_cfg_file:
                waveform_cfg_file.write(
                    json.dumps(new_waveform_generation,
                               cls=ObjectEncoder,
                               indent=2,
                               sort_keys=True))

            # set the path for the (single-)report
            os.environ["TARGET_FOLDER"] = os.path.join(
                os.environ["RESULT_OUTPUT_DIR"],
                os.environ["ME_REPORT_FOLDER"], time.strftime("%Y%m%d_%H%M%S"))
            my_print("target folder: " + str(os.environ["TARGET_FOLDER"]))

            # always check for the keep_waveform setting from the json file ==> If disabled, we always rerun the complete simulation
            if multi_exec.keep_waveform and keep_std_digsim:
                my_print("Keep DIGSIM!", EscCodes.OKBLUE,
                         override_print_env_flag)
                execute_make_cmd("make me_involution")
            elif multi_exec.keep_waveform and keep_crossings:
                my_print("Keep CROSSINGS!", EscCodes.OKBLUE,
                         override_print_env_flag)
                execute_make_cmd("make me_digsim")
            elif multi_exec.keep_waveform and keep_spice:
                my_print("Keep SPICE!", EscCodes.OKBLUE,
                         override_print_env_flag)
                execute_make_cmd("make me_crossings")
            elif multi_exec.keep_waveform and keep_waveform_local:
                my_print("Keep Waveform!", EscCodes.OKBLUE,
                         override_print_env_flag)
                execute_make_cmd("make clean")
                execute_make_cmd(
                    "make all"
                )  # Careful, altough we clean up, we still want to keep the waveform
            else:
                my_print("Complete run!", EscCodes.OKBLUE,
                         override_print_env_flag)
                # Clean up after previous simulation
                execute_make_cmd("make clean")

                # Now execute the simulation
                execute_make_cmd("make all")

            # Add a file with the "configuration id" - required for reporting
            if not (os.getenv('SKIP_SIMULATION', None)):
                with open(
                        os.path.join(os.environ["TARGET_FOLDER"], "config_id"),
                        "w") as cfg_id_file:
                    cfg_id_file.write(str(config_num))

            #increment all the counters in the dict (needs to be done in a sorted way)
            # sorted is also required, because properties where the waveform can be kept have to be in the innermost loop
            overflow = False

            for key in sorted(property_dict):
                overflow = False

                property_dict[key] += 1
                if property_dict[key] == length_dict[key]:
                    overflow = True
                    #print "Overflow at: " + key + ""
                    property_dict[key] = 0
                    # We had an overflow. Now check if we can keep the waveform (true as long this is not the last parameter that keeps the waveform)

                    keep_std_digsim = calc_keep_property(
                        key, last_key_to_keep_stddigsim, True)
                    keep_crossings = calc_keep_property(
                        key, last_key_to_keep_crossings, True)
                    keep_spice = calc_keep_property(key,
                                                    last_key_to_keep_spice,
                                                    True)
                    keep_waveform_local = calc_keep_property(
                        key, last_key_to_keep_waveform, True)
                    keep_group_count = calc_keep_property(
                        key, last_key_to_keep_group_count, True)
                    keep_reference_group_count = calc_keep_property(
                        key, last_key_to_keep_reference_count, True)
                else:
                    keep_std_digsim = calc_keep_property(
                        key, last_key_to_keep_stddigsim, False)
                    keep_crossings = calc_keep_property(
                        key, last_key_to_keep_crossings, False)
                    keep_spice = calc_keep_property(key,
                                                    last_key_to_keep_spice,
                                                    False)
                    keep_waveform_local = calc_keep_property(
                        key, last_key_to_keep_waveform, False)
                    keep_group_count = calc_keep_property(
                        key, last_key_to_keep_group_count, False)
                    keep_reference_group_count = calc_keep_property(
                        key, last_key_to_keep_reference_count, False)

                    break  # no need to increment the other properties

            if len(property_dict) == 0:
                # if we just want to execute the standard configuration multiple times, we have an overflow after each simulation run:
                overflow = True

            if not keep_group_count:
                # we need to get the maximum value from the dict (because we could configure ABSOLUTE, PERCENT, ABSOLUTE)
                new_group_id = int(os.environ["ME_group"]) + 1
                if not me_group_param_mode_dict:
                    if len(me_group_param_mode_dict.values()) == 0:
                        new_group_id = 0
                    else:
                        new_group_id = max(
                            me_group_param_mode_dict.values()) + 1
                os.environ["ME_group"] = str(new_group_id)
                # Reset our datastructures
                me_group_param_mode_dict = dict()
                prev_param_mode = None

            if not keep_reference_group_count:
                os.environ["ME_reference_group"] = str(
                    int(os.environ["ME_reference_group"]) + 1)

            # "first" property has "overflowed", we are done
            if overflow:
                #print "Iterated over all configurations"
                break

            curr_sim_num = curr_sim_num + 1
def causality_checker(circuit_folder, default_gate_config_file_path,
                      circuit_gate_config_file_path, structure_file_path,
                      characterization_conf_file_path, sdf_file_path,
                      instance_mapping_file_path):

    # structure file
    structure_file_path = os.path.join(circuit_folder, structure_file_path)
    structure = read_circuit_structure(structure_file_path)

    # sdf file
    sdf_file_path = os.path.join(circuit_folder, sdf_file_path)
    dinf_dict = read_sdf_file(sdf_file_path)

    # instance_mapping
    instance_mapping_file_path = os.path.join(circuit_folder,
                                              instance_mapping_file_path)

    instance_mapping = dict()
    if os.path.exists(instance_mapping_file_path):
        with open(instance_mapping_file_path) as f:
            instance_mapping = json.load(f)

    # gate config files (general and circuit specific files)
    default_gate_config_file_path = os.path.join(
        circuit_folder, default_gate_config_file_path)
    circuit_gate_config_file_path = os.path.join(
        circuit_folder, circuit_gate_config_file_path)
    gate_config = read_gate_config(default_gate_config_file_path,
                                   circuit_gate_config_file_path)

    # Characterization config file (the tree)
    characterization_conf_file_path = os.path.join(
        circuit_folder, characterization_conf_file_path)
    char_conf = None
    with open(characterization_conf_file_path) as json_file:
        char_conf = json.load(json_file)

    # Algorithm: Go over the tree in preorder
    importer = DictImporter()
    dependency_tree = importer.import_(char_conf['dependency_tree'])

    node_cnt = 0

    for node in PreOrderIter(dependency_tree):
        if node.name == 'Top':
            continue

        print("-----------", node_cnt)
        node_cnt = node_cnt + 1

        cellname = get_cellname(char_conf, node.name)

        # Find the cell with cellname
        curr_cell = find_cell_in_structure(structure, cellname)

        if not curr_cell:
            print("Did not find a cell for {}".format(cellname))
            continue

        assert (curr_cell.instance in instance_mapping)
        sdf_instance_name = instance_mapping[curr_cell.instance]
        (d_inf_up, d_inf_do) = dinf_dict[sdf_instance_name]

        curr_gate = gate_config[curr_cell.cell_type]

        # now we need to check causality from this cell to all successor cells
        for succ in node.children:
            succ_cellname = get_cellname(char_conf, succ.name)
            succ_cell = find_cell_in_structure(structure, succ_cellname)

            print(curr_cell, succ_cell)

            if succ_cell:

                succ_gate = gate_config[succ_cell.cell_type]
                assert (succ_gate.function.lower() == "not"
                        or succ_gate.function == "")
                succ_cell_inverting = succ_gate.function.lower() == "not"

                if succ_cell_inverting:
                    succ_pure_delay_up = extract_delay_from_structure(
                        succ_cell.pure_delay_down)
                    succ_pure_delay_do = extract_delay_from_structure(
                        succ_cell.pure_delay_up)
                else:
                    succ_pure_delay_up = extract_delay_from_structure(
                        succ_cell.pure_delay_up)
                    succ_pure_delay_do = extract_delay_from_structure(
                        succ_cell.pure_delay_down)

                succ_pure_delay = extract_delay_from_structure(
                    succ_cell.pure_delay)
                succ_delta_plus = succ_pure_delay_up - succ_pure_delay
                succ_delta_minus = succ_pure_delay_do - succ_pure_delay

                curr_pure_delay = extract_delay_from_structure(
                    curr_cell.pure_delay)
                curr_pure_delay_up = extract_delay_from_structure(
                    curr_cell.pure_delay_up)
                curr_pure_delay_do = extract_delay_from_structure(
                    curr_cell.pure_delay_down)
                curr_delta_plus = curr_pure_delay_up - curr_pure_delay
                curr_delta_minus = curr_pure_delay_do - curr_pure_delay

                # If none of them is > 0, we are definitely acausal
                assert (succ_delta_plus >= 0 or succ_delta_minus >= 0)
                # One must be <= 0, the other one >= 0
                assert ((succ_delta_plus >= 0 and succ_delta_minus <= 0)
                        or (succ_delta_plus <= 0 and succ_delta_minus >= 0))
                assert (d_inf_up > 0)
                assert (d_inf_do > 0)

                assert (curr_pure_delay < d_inf_up)
                assert (curr_pure_delay < d_inf_do)

                # No algorithm yet implemented to automatically fix acausality, this is currently done manually
                # But of course, the simplest solution would be a binary search on the negative delta value and stop once we have two subsequent iteration
                # where the previous one was causal and the current acausal, and the difference between the two delta values is less than 1 fs (this is the highest possible precision of Questa)
                # TODO: Not sure anymore if this simple approach would be sufficient
                (causal_up, causal_do) = check_causality(
                    curr_gate, curr_cell, d_inf_up, d_inf_do, curr_pure_delay,
                    succ_delta_plus, succ_delta_minus, curr_delta_plus,
                    curr_delta_minus)
                # plot_involution(curr_gate, curr_cell, d_inf_up, d_inf_do, curr_pure_delay, succ_delta_plus, succ_delta_minus, curr_delta_plus, curr_delta_minus)
                print(succ_cell_inverting, cellname, succ_cellname,
                      "d_inf_up: ", d_inf_up, "d_inf_do: ", d_inf_do,
                      "curr_pure_delay: ", curr_pure_delay,
                      "curr_delta_plus: ", curr_delta_plus,
                      "curr_delta_minus:", curr_delta_minus,
                      "succ_delta_plus: ", succ_delta_plus,
                      "succ_delta_minus: ", succ_delta_minus, "causal_up: ",
                      causal_up, "causal_do: ", causal_do)
                # We need at least 1 fs, otherwise we are potentially due to rounding at 0, which is not strictly causal any more
                assert (causal_up >= 0 and causal_do >= 0)

            else:
                # Only the current dmin needs to be > 0
                curr_pure_delay = extract_delay_from_structure(
                    curr_cell.pure_delay)
                assert (curr_pure_delay > 0)