def set_waits(testbench_config, testbench): """ Add the logic to handle wait conditions Args: testbench_config (Testbench): The testbench configuration testbench (str): The testbench being generated Returns: str: Updated testbench """ # TODO need to handle this if wait_conditions is empty (sv will error out) wait_conditions = testbench_config.get_from_dut("wait_conditions") replace_str = "" leading_spaces = include.get_indentation("SONAR_IF_ELSE_WAIT", testbench) for condition in wait_conditions: if replace_str != "": replace_str += leading_spaces + "else " replace_str += ('if(interfaceType_par == "' + condition["key"] + '") begin\n') regex_variable = re.compile(r"\$\d+") condition_str = condition["condition"] variables = re.findall(regex_variable, condition["condition"]) for variable in variables: condition_str = condition_str.replace(variable, f"args[{variable[1:]}]") if not condition_str.endswith(";"): condition_str += ";" replace_str += leading_spaces + include.TAB_SIZE + condition_str + "\n" replace_str += leading_spaces + "end\n" testbench = include.replace_in_testbenches(testbench, "SONAR_IF_ELSE_WAIT", replace_str[:-1]) return testbench
def set_signals(testbench_config, testbench): """ Add commands to interact with signals when reading the data file Args: testbench_config (Testbench): The testbench configuration testbench (str): The testbench being generated Returns: str: Updated testbench """ dut = testbench_config.modules["DUT"] signals_in = dut.ports.get_signals("input") ifelse_signal = "" leading_spaces = include.get_indentation("SONAR_IF_ELSE_SIGNAL", testbench) for signal in signals_in: if "type" not in signal: if ifelse_signal != "": ifelse_signal += leading_spaces + "else " ifelse_signal += ('if(!strcmp(interfaceType, "' + signal.name + '")){\n') ifelse_signal += (leading_spaces + TAB_SIZE + signal.name + " = args[0];\n") ifelse_signal += leading_spaces + "}\n" # TODO fix this # ifelse_signal = "" # clear this since it's not being used right now testbench = include.replace_in_testbenches(testbench, "SONAR_IF_ELSE_SIGNAL", ifelse_signal[:-1]) return testbench
def declare_signals(testbench_config, testbench): """ Declare all signals used to connect to the DUT Args: testbench_config (Testbench): The testbench configuration testbench (str): The testbench being generated Returns: str: Updated testbench """ dut = testbench_config.modules["DUT"] clocks_in = dut.ports.get_clocks("input") signals = dut.ports.get_signals() resets = dut.ports.get_resets() interfaces = testbench_config.get_from_dut("interfaces") tb_signal_list = "" max_signal_size = 0 leading_spaces = include.get_indentation("SONAR_TB_SIGNAL_LIST", testbench) for clock in clocks_in: if tb_signal_list != "": tb_signal_list += leading_spaces tb_signal_list += "logic " + clock.name + ";\n" for signal in itertools.chain(signals, resets): if int(signal.size) == 1: tb_signal_list += leading_spaces + "logic " + signal.name + ";\n" else: tb_signal_list += (leading_spaces + "logic [" + str(int(signal.size) - 1) + ":0] " + signal.name + ";\n") if int(signal.size) > max_signal_size: max_signal_size = int(signal.size) for interface in interfaces: for signal_type, signal in interface.signals.items(): if int(signal.size) == 1: tb_signal_list += (leading_spaces + "logic " + interface.name + "_" + signal_type + ";\n") else: tb_signal_list += (leading_spaces + "logic [" + str(int(signal.size) - 1) + ":0] " + interface.name + "_" + signal_type + ";\n") if int(signal.size) > max_signal_size: max_signal_size = int(signal.size) testbench = include.replace_in_testbenches(testbench, "SONAR_TB_SIGNAL_LIST", tb_signal_list[:-1]) testbench = include.replace_in_testbenches(testbench, "SONAR_MAX_DATA_SIZE", max_signal_size) return testbench
def instantiate_dut(testbench_config, testbench): """ Instantiate the device-under-test Args: testbench_config (Testbench): The testbench configuration testbench (str): The testbench being generated Returns: str: Updated testbench """ # pylint: disable=too-many-locals dut = testbench_config.modules["DUT"] clocks_in = dut.ports.get_clocks("input") signals_in = dut.ports.get_signals("input") resets_in = dut.ports.get_resets("input") signals_out = dut.ports.get_signals("output") parameters = testbench_config.get_from_dut("parameters") interfaces = testbench_config.get_from_dut("interfaces") dut_inst = "" leading_spaces = include.get_indentation("SONAR_DUT_INST", testbench) module_name = testbench_config.metadata["Module_Name"] if parameters: dut_inst += testbench_config.metadata["Module_Name"] + " #(\n" for parameter in parameters[:-1]: dut_inst += f"{leading_spaces}{include.TAB_SIZE}.{parameter[0]}({str(parameter[1])}),\n" dut_inst += f"{leading_spaces}{include.TAB_SIZE}.{parameters[-1][0]}({str(parameters[-1][1])})\n" dut_inst += f"{leading_spaces}) {module_name}_i (\n" else: dut_inst += f"{module_name} {module_name}_i (\n" for signal in itertools.chain(clocks_in, resets_in): dut_inst += f"{leading_spaces}{include.TAB_SIZE}.{signal.name}({signal.name}),\n" dut_type = testbench_config.modules["DUT"].type for signal in itertools.chain(signals_in, signals_out): port_name = modify_signal_name(signal.name, dut_type) dut_inst += ( f"{leading_spaces}{include.TAB_SIZE}.{port_name}({signal.name}),\n" ) for interface in interfaces: for signal_type, signal in interface.signals.items(): dut_inst += (leading_spaces + include.TAB_SIZE + "." + interface.name + "_" + signal.name + "(" + interface.name + "_" + signal_type + "),\n") dut_inst = dut_inst[:-2] + "\n" dut_inst += leading_spaces + ");" testbench = include.replace_in_testbenches(testbench, "SONAR_DUT_INST", dut_inst) return testbench
def set_signals(testbench_config, testbench, used_interfaces): """ When reading commands, add the logic to assign values to individual signals Args: testbench_config (Testbench): The testbench configuration testbench (str): The testbench being generated used_interfaces (dict): Each used interface appears once Returns: str: Updated testbench """ dut = testbench_config.modules["DUT"] signals_in = dut.ports.get_signals("input") resets_in = dut.ports.get_resets("input") interfaces = testbench_config.get_from_dut("interfaces") ifelse_signal = "" leading_spaces = include.get_indentation("SONAR_IF_ELSE_SIGNAL", testbench) for signal in itertools.chain(signals_in, resets_in): if ifelse_signal != "": ifelse_signal += leading_spaces + "else " ifelse_signal += ('if(interfaceType_par == "' + signal.name + '") begin\n' + leading_spaces + include.TAB_SIZE + signal.name + " = args[0];\n" + leading_spaces + "end\n") for interface in interfaces: curr_interface = used_interfaces[interface.interface_type] for signal_type, signal in interface.signals.items(): if (interface.direction in ("slave", "mixed") and signal_type in curr_interface.signals["output"]) or ( interface.direction in ("master") and signal_type in curr_interface.signals["input"]): if ifelse_signal != "": ifelse_signal += leading_spaces + "else " ifelse_signal += ('if(interfaceType_par == "' + interface.name + "_" + signal_type + '") begin\n' + leading_spaces + include.TAB_SIZE + interface.name + "_" + signal_type + " = args[0];\n" + leading_spaces + "end\n") testbench = include.replace_in_testbenches(testbench, "SONAR_IF_ELSE_SIGNAL", ifelse_signal[:-1]) return testbench
def declare_signals(testbench_config, testbench): """ Declare and instantiate the variables used for the signals in the testbench Args: testbench_config (Testbench): The testbench configuration testbench (str): The testbench being generated Returns: str: Updated testbench """ dut = testbench_config.modules["DUT"] signals = dut.ports.get_signals() interfaces = testbench_config.get_from_dut("interfaces") tb_signal_list = "" max_signal_size = 0 leading_spaces = include.get_indentation("SONAR_TB_SIGNAL_LIST", testbench) for signal in signals: # TODO fix leading spaces on first entry tb_signal_list += (leading_spaces + "ap_uint<" + str(signal.size) + "> " + signal.name + ";\n") if int(signal.size) > max_signal_size: max_signal_size = int(signal.size) for interface in interfaces: if isinstance(interface, AXI4LiteSlave): data_width_c = interface.get_signal("wdata").size for regs in interface.registers: tb_signal_list += (leading_spaces + f"ap_uint<{data_width_c}> {regs};\n") elif isinstance(interface, AXI4Stream): tb_signal_list += (leading_spaces + interface.iClass + " " + interface.name + ";\n") tb_signal_list += (leading_spaces + interface.flit + " " + interface.name + "_flit;\n") testbench = include.replace_in_testbenches(testbench, "SONAR_TB_SIGNAL_LIST", tb_signal_list[:-1]) testbench = include.replace_in_testbenches(testbench, "SONAR_MAX_DATA_SIZE", max_signal_size) return testbench
def add_signal_endpoints(testbench_config, testbench): """ Add signals' sources/sinks Args: testbench_config (Testbench): The testbench configuration testbench (str): The testbench being generated Returns: str: The testbench """ endpoints_str = "" leading_spaces = include.get_indentation("SONAR_INCLUDE_ENDPOINTS", testbench) all_endpoints = testbench_config.get_from_dut("endpoints") for signal_index, endpoint_dict in enumerate(all_endpoints.items()): signal, endpoints = endpoint_dict if endpoints_str != "": endpoints_str += leading_spaces endpoints_str = ( f"logic [$$size-1:0] $$name_endpoint[{len(endpoints)}];\n") endpoints_str += ( leading_spaces + f"assign $$name = $$name_endpoint[{signal_index}];\n") for endpoint in endpoints: endpoints_str += endpoint.instantiate(leading_spaces) endpoints_str = endpoints_str.replace( "$$size", str(endpoint.arguments["size"])) endpoints_str = endpoints_str.replace("$$name", signal) endpoints_str = endpoints_str.replace("$$endpointIndex", str(signal_index)) testbench = include.replace_in_testbenches(testbench, "SONAR_INCLUDE_ENDPOINTS", endpoints_str[:-1]) return testbench
def set_interfaces(testbench_config, testbench): """ When reading commands, add the logic to assign interact with interfaces Args: testbench_config (Testbench): The testbench configuration testbench (str): The testbench being generated Returns: str: Updated testbench """ interfaces = testbench_config.get_from_dut("interfaces") replace_str = "" leading_spaces = include.get_indentation("SONAR_ELSE_IF_INTERFACE_IN", testbench) for index, interface in enumerate(interfaces): if replace_str != "": replace_str += leading_spaces replace_str += 'else if(interfaceType_par == "$$name") begin\n' replace_str += (leading_spaces + include.TAB_SIZE + f"$$interfaceType_$$index(args, retval[{index}]);\n") replace_str += (leading_spaces + include.TAB_SIZE + f"if(retval[{index}] != 0) begin\n") replace_str += (leading_spaces + include.TAB_SIZE * 2 + "error = 1'b1;\n") replace_str += leading_spaces + include.TAB_SIZE * 2 + "$stop;\n" replace_str += leading_spaces + include.TAB_SIZE + "end\n" replace_str += leading_spaces + "end\n" replace_str = include.replace_variables(replace_str, interface) testbench = include.replace_in_testbenches(testbench, "SONAR_ELSE_IF_INTERFACE_IN", replace_str[:-1]) testbench = include.replace_in_testbenches(testbench, "SONAR_INTERFACES_COUNT", len(interfaces)) return testbench
def instantiate_exerciser(testbench_config, testbench): """ Instantiate the Exerciser module Args: testbench_config (Testbench): The testbench configuration testbench (str): The testbench being generated Returns: str: Updated testbench """ dut = testbench_config.modules["DUT"] clocks_in = dut.ports.get_clocks("input") signals = dut.ports.get_signals() resets = dut.ports.get_resets() interfaces = testbench_config.get_from_dut("interfaces") exerciser_int = "" leading_spaces = include.get_indentation("SONAR_EXERCISER_INT", testbench) exerciser_int += "exerciser exerciser_i(\n" for clock in clocks_in: exerciser_int += (leading_spaces + include.TAB_SIZE + "." + clock.name + "(" + clock.name + "),\n") for interface in interfaces: for signal_type, signal in interface.signals.items(): exerciser_int += (leading_spaces + include.TAB_SIZE + "." + interface.name + "_" + signal_type + "(" + interface.name + "_" + signal_type + "),\n") for signal in itertools.chain(signals, resets): exerciser_int += (leading_spaces + include.TAB_SIZE + "." + signal.name + "(" + signal.name + "),\n") exerciser_int = exerciser_int[:-2] + "\n" exerciser_int += leading_spaces + ");\n" testbench = include.replace_in_testbenches(testbench, "SONAR_EXERCISER_INT", exerciser_int[:-1]) return testbench
def set_interfaces(testbench_config, testbench): """ Add commands to interact with interfaces when reading the data file Args: testbench_config (Testbench): The testbench configuration testbench (str): The testbench being generated Returns: str: Updated testbench """ dut = testbench_config.modules["DUT"] interfaces_slave = dut.ports.get_interfaces("slave") interfaces_master = dut.ports.get_interfaces("master") interfaces_mixed = dut.ports.get_interfaces("mixed") replace_str = "" leading_spaces = include.get_indentation("SONAR_ELSE_IF_INTERFACE_IN", testbench) for interface in itertools.chain(interfaces_slave, interfaces_mixed): if replace_str != "": replace_str += leading_spaces + "else " replace_str += ('if(!strcmp(interfaceType, "' + interface.name + '")){\n') # in cpp, only consider first endpoint endpoint = interface.endpoints[0] replace_str = include.command_var_replace( replace_str, interface, leading_spaces + TAB_SIZE, "cpp", "master", endpoint, ) replace_str += leading_spaces + "}\n" testbench = include.replace_in_testbenches(testbench, "SONAR_ELSE_IF_INTERFACE_IN", replace_str[:-1]) replace_str_2 = "" leading_spaces = include.get_indentation("SONAR_ELSE_IF_INTERFACE_OUT", testbench) for interface in interfaces_master: if replace_str != "" or replace_str_2 != "": replace_str_2 += (leading_spaces + "else " ) # TODO fix this on first one. too many spaces replace_str_2 += ('if(!strcmp(interfaceType, "' + interface.name + '")){\n') # in cpp, only consider first endpoint endpoint = interface.endpoints[0] replace_str_2 = include.command_var_replace( replace_str_2, interface, leading_spaces + TAB_SIZE, "cpp", "slave", endpoint, ) replace_str_2 += leading_spaces + "}\n" testbench = include.replace_in_testbenches(testbench, "SONAR_ELSE_IF_INTERFACE_OUT", replace_str_2[:-1]) return testbench
def add_interfaces(testbench_config, testbench, directory): """ Add interfaces and their sources/sinks Args: testbench_config (Testbench): The testbench configuration testbench (str): The testbench being generated directory (str): Path to the directory to place generated files Returns: str: The testbench """ # pylint: disable=too-many-locals, too-many-statements endpoints_str = "" imports_str = "" used_interfaces = {} leading_spaces = include.get_indentation( "SONAR_INCLUDE_INTERFACE_ENDPOINTS", testbench) interfaces = testbench_config.get_from_dut("interfaces") # endpoints = testbench_config.get_from_dut("endpoints_flat") for interface_index, interface in enumerate(interfaces): if interface.interface_type not in used_interfaces: used_interfaces[interface.interface_type] = True imports_str += interface.core.import_packages_global() endpoint_str = "" if interface.direction == "master": signals = interface.core.signals["input"] else: signals = interface.core.signals["output"] action = { "signals": signals, "commands": [ f"logic [$$size-1:0] $$name_$$signal_endpoint[{len(interface.endpoints)}];\n" f"assign $$name_$$signal = $$name_$$signal_endpoint[endpoint_select[{interface_index}]];\n" ], } for command in action["commands"]: endpoint_str = include.replace_signals( interface, action, command, endpoint_str, "", interface.core.args["sv"], ) for endpoint_index, endpoint in enumerate(interface.endpoints): endpoint_str = add_prologue(endpoint_str, endpoint, interface) endpoint_str = add_initial_blocks(endpoint_str, endpoint, interface) endpoint_str = instantiate_endpoint_ips(endpoint_str, endpoint) for key in endpoint.actions["sv"].keys(): # for key, _commands in endpoint.core.actions["sv"].items(): endpoint_str += f"task $$interfaceType_$$index_{key}_$$endpointIndex(input logic [MAX_DATA_SIZE-1:0] args [MAX_ARG_NUM], output int retval);\n" endpoint_str += include.TAB_SIZE + "retval = 0;\n" endpoint_str = include.command_var_replace( endpoint_str, interface, include.TAB_SIZE, "sv", key, endpoint, ) endpoint_str += "endtask\n" endpoint_str = endpoint_str.replace("$$endpointIndex", str(endpoint_index)) endpoint.source_tcl(interface, directory) for key, value in endpoint.arguments.items(): endpoint_str = endpoint_str.replace(f"$${key}", str(value)) endpoint_str += "task $$interfaceType_$$index(input logic [MAX_DATA_SIZE-1:0] args [MAX_ARG_NUM], output int retval);\n" endpoint_str += include.TAB_SIZE + "retval = 0;\n" # for key, _commands in endpoint.core.actions["sv"].items(): for endpoint_index, endpoint in enumerate(interface.endpoints): imports_str += endpoint.import_packages_local(interface) for key in endpoint.actions["sv"].keys(): endpoint_str += include.TAB_SIZE if endpoint_index != 0: endpoint_str += "else " arg_index = get_nth_index(endpoint.actions["sv"], key) endpoint_str += f"if (endpoint_select[{interface_index}] == {endpoint_index} && args[0] == {arg_index}) begin\n" endpoint_str += ( include.TAB_SIZE * 2 + f"$$interfaceType_$$index_{key}_$$endpointIndex(args, retval);\n" ) endpoint_str += include.TAB_SIZE + "end\n" endpoint_str = endpoint_str.replace("$$endpointIndex", str(endpoint_index)) endpoint_str += f"{include.TAB_SIZE}else begin\n" endpoint_str += include.TAB_SIZE * 2 + '$error("Unknown command!");\n' endpoint_str += f"{include.TAB_SIZE}end\n" endpoint_str += "endtask\n" endpoint_str = include.replace_variables(endpoint_str, interface) filename = interface.interface_type + f"_{interface.index}.sv" filepath = os.path.join(directory, filename) with open(filepath, "w+") as f: f.write(endpoint_str) if endpoints_str != "": endpoints_str += leading_spaces endpoints_str += f'`include "{filename}"\n' testbench = include.replace_in_testbenches( testbench, "SONAR_INCLUDE_INTERFACE_ENDPOINTS", endpoints_str) testbench = include.replace_in_testbenches(testbench, "SONAR_IMPORT_PACKAGES", imports_str[:-1]) return testbench
def add_exerciser_ports(testbench_config, testbench, used_interfaces): """ Add the ports of the Exerciser Args: testbench_config (Testbench): The testbench configuration testbench (str): The testbench being generated used_interfaces (dict): Each used interface appears once Returns: str: Updated testbench """ def add_interfaces(exerciser_ports): interfaces = testbench_config.get_from_dut("interfaces_dict") for direction in ["slave", "master", "mixed"]: for interface in interfaces[direction]: curr_interface = used_interfaces[interface.interface_type] for signal_type, signal in interface.signals.items(): exerciser_ports += leading_spaces if direction in ["slave", "mixed"]: if signal_type in curr_interface.signals["input"]: exerciser_ports += "input " else: exerciser_ports += "output " init_signals.append(interface.name + "_" + signal_type) else: if signal_type in curr_interface.signals["output"]: exerciser_ports += "input " else: exerciser_ports += "output " init_signals.append(interface.name + "_" + signal_type) exerciser_ports += "logic " if int(signal.size) != 1: exerciser_ports += ("[" + str(int(signal.size) - 1) + ":0] ") exerciser_ports += (interface.name + "_" + signal_type + ",\n") return exerciser_ports def resolve_init_signals(): init_commands = [] for init_signal in init_signals: init_commands.append({"signal": {"name": init_signal, "value": 0}}) for i, vector in enumerate(testbench_config.vectors): for j, thread in enumerate(vector.threads): for k, command in enumerate(thread.commands): if ("macro" in command and command["macro"] == "INIT_SIGNALS"): testbench_config.vectors[i].threads[j].commands[k][ "commands"] = init_commands dut = testbench_config.modules["DUT"] clocks_in = dut.ports.get_clocks("input") signals_in = dut.ports.get_signals("input") resets_in = dut.ports.get_resets("input") signals_out = dut.ports.get_signals("output") init_signals = [] exerciser_ports = "" leading_spaces = include.get_indentation("SONAR_EXERCISER_PORTS", testbench) for clock in clocks_in: if exerciser_ports != "": exerciser_ports += leading_spaces exerciser_ports += "output logic " + clock.name + ",\n" exerciser_ports = add_interfaces(exerciser_ports) for signal in itertools.chain(signals_in, resets_in): exerciser_ports += leading_spaces + "output logic " if int(signal.size) != 1: exerciser_ports += "[" + str(int(signal.size) - 1) + ":0] " exerciser_ports += signal.name + ",\n" init_signals.append(signal.name) for signal in signals_out: exerciser_ports += leading_spaces + "input logic " if int(signal.size) != 1: exerciser_ports += "[" + str(int(signal.size) - 1) + ":0] " exerciser_ports += signal.name + ",\n" testbench = include.replace_in_testbenches(testbench, "SONAR_EXERCISER_PORTS", exerciser_ports[:-2]) resolve_init_signals() return testbench, testbench_config