Source code for uatg.test_generator

# See LICENSE.incore for license details

import os
import glob
from shutil import rmtree
from getpass import getuser
from datetime import datetime
import ruamel.yaml as yaml
import uatg
from uatg.utils import create_plugins, generate_test_list
from uatg.utils import create_linker, create_model_test_h
from uatg.utils import join_yaml_reports, generate_sv_components
from uatg.utils import list_of_modules, rvtest_data
from yapsy.PluginManager import PluginManager
from uatg.log import logger


[docs]def generate_tests(work_dir, linker_dir, modules, config_dict, test_list, modules_dir): """ The function generates ASM files for all the test classes specified within the module_dir. The user can also select the modules for which he would want the tests to be generated for. The YAPSY plugins for the tests are generated by the function automatically. The tests are created within the work directory passed by the user. A test_list is also created in the yaml format by the function. The test generator also creates a linker file as well as the header files for running the ASM files on the DUT, when required. Finally, the test generator only generates the tests whose targets are implemented in the DUT. """ uarch_dir = os.path.dirname(uatg.__file__) if work_dir: pass else: work_dir = os.path.abspath((os.path.join(uarch_dir, '../work/'))) os.makedirs(work_dir, exist_ok=True) logger.info(f'uatg dir is {uarch_dir}') logger.info(f'work_dir is {work_dir}') inp_yaml = config_dict try: isa = inp_yaml['ISA'] except Exception as e: logger.error(e) logger.error('Exiting UATG') exit(0) if modules == ['all']: logger.debug(f'Checking {modules_dir} for modules') modules = list_of_modules(modules_dir) logger.debug(f'The modules are {modules}') test_list_dict = {} logger.info('****** Generating Tests ******') for module in modules: module_dir = os.path.join(modules_dir, module) work_tests_dir = os.path.join(work_dir, module) try: module_params = config_dict[module] except KeyError: # logger.critical("The {0} module is not in the dut config_file", # format(module)) module_params = {} module_params['isa'] = isa logger.debug(f'Directory for {module} is {module_dir}') logger.info(f'Starting plugin Creation for {module}') create_plugins(plugins_path=module_dir) logger.info(f'Created plugins for {module}') username = getuser() time = ((str(datetime.now())).split("."))[0] license_str = f'# Licensing information can be found at ' \ f'LICENSE.incore\n# Test generated by user - {username}' \ f' at {time}\n\n' includes = f'#include \"model_test.h\" \n#include \"arch_test.h\"\n' test_entry = f'RVTEST_ISA(\"{isa}\")\n\n.section .text.init\n.globl' \ f' rvtest_entry_point\nrvtest_entry_point:' rvcode_begin = '\nRVMODEL_BOOT\nRVTEST_CODE_BEGIN\n' rvcode_end = '\nRVTEST_CODE_END\nRVMODEL_HALT\n\n' rvtest_data_begin = '\nRVTEST_DATA_BEGIN\n' rvtest_data_end = '\nRVTEST_DATA_END\n\n' rvmodel_data_begin = '\nRVMODEL_DATA_BEGIN\n' rvmodel_data_end = '\nRVMODEL_DATA_END\n\n' manager = PluginManager() manager.setPluginPlaces([module_dir]) # plugins are stored in module_dir manager.collectPlugins() # check if prior test files are present and remove them. create new dir. if (os.path.isdir(work_tests_dir)) and \ os.path.exists(work_tests_dir): rmtree(work_tests_dir) os.mkdir(work_tests_dir) logger.debug(f'Generating assembly tests for {module}') # Loop around and find the plugins and writes the contents from the # plugins into an asm file for plugin in manager.getAllPlugins(): check = plugin.plugin_object.execute(module_params) name = (str(plugin.plugin_object).split(".", 1)) test_name = ((name[1].split(" ", 1))[0]) if check: asm_body = plugin.plugin_object.generate_asm() # Adding License, includes and macros asm = license_str + includes + test_entry # Appending Coding Macros & Instructions asm += rvcode_begin + asm_body + rvcode_end # Appending RVTEST_DATA macros and data values asm += rvtest_data_begin + rvtest_data(bit_width=32, num_vals=1, random=True, signed=False, align=4) asm += rvtest_data_end # Appending RVMODEL macros asm += rvmodel_data_begin + rvmodel_data_end os.mkdir(os.path.join(work_tests_dir, test_name)) with open( os.path.join(work_tests_dir, test_name, test_name + '.S'), "w") as f: f.write(asm) logger.debug(f'Generating test for {test_name}') else: logger.critical(f'Skipped {test_name}') logger.debug(f'Finished Generating Assembly Tests for {module}') if test_list: logger.info(f'Creating test_list for the {module}') test_list_dict.update( generate_test_list(work_tests_dir, uarch_dir, test_list_dict)) logger.info('****** Finished Generating Tests ******') if linker_dir and os.path.isfile(os.path.join(linker_dir, 'link.ld')): logger.debug('Using user specified linker') else: create_linker(target_dir=work_dir) logger.debug(f'Creating a linker file at {work_dir}') if linker_dir and os.path.isfile(os.path.join(linker_dir, 'model_test.h')): logger.debug('Using user specified model_test file') else: create_model_test_h(target_dir=work_dir) logger.debug(f'Creating Model_test.h file at {work_dir}') if test_list: logger.info('Test List was generated by UATG. You can find it in ' 'the work dir ') else: logger.info('Test list will not be generated by uatg') if test_list.lower() == 'true': with open(work_dir + '/' + 'test_list.yaml', 'w') as outfile: yaml.dump(test_list_dict, outfile)
[docs]def generate_sv(work_dir, config_dict, modules, modules_dir, alias_dict): """ The generate_sv function dumps the covergroups written by the user into a 'coverpoints.sv' file present within the 'sv_top' directory within the work directory. This function dumps into an SV file only if the test_class contains the generate_covergroups method. This function, like generate_asm also allows to select the modules for which covergroups are to be generated. In addition, the method also takes in an alias_dict which can be used to alias the BSV signal names to something even more comprehensible. """ uarch_dir = os.path.dirname(uatg.__file__) if work_dir: pass else: work_dir = os.path.abspath((os.path.join(uarch_dir, '../work/'))) if modules == ['all']: logger.debug(f'Checking {modules_dir} for modules') modules = list_of_modules(modules_dir) inp_yaml = config_dict try: isa = inp_yaml['ISA'] except Exception as e: logger.error(e) logger.error('Exiting UATG') exit(0) logger.info('****** Generating Covergroups ******') sv_dir = os.path.join(work_dir, 'sv_top') os.makedirs(sv_dir, exist_ok=True) # generate the tbtop and interface files generate_sv_components(sv_dir, alias_dict) logger.debug("Generated tbtop, defines and interface files") sv_file = os.path.join(sv_dir, 'coverpoints.sv') if os.path.isfile(sv_file): logger.debug("Removing Existing coverpoints SV file") os.remove(sv_file) for module in modules: logger.debug(f'Generating CoverPoints for {module}') module_dir = os.path.join(modules_dir, module) try: module_params = inp_yaml[module] except KeyError: module_params = {} module_params['isa'] = isa manager = PluginManager() manager.setPluginPlaces([module_dir]) manager.collectPlugins() for plugin in manager.getAllPlugins(): _check = plugin.plugin_object.execute(module_params) _name = (str(plugin.plugin_object).split(".", 1)) _test_name = ((_name[1].split(" ", 1))[0]) if _check: try: _sv = plugin.plugin_object.generate_covergroups(alias_dict) with open(sv_file, "a") as f: logger.info( f'Generating coverpoints SV file for {_test_name}') f.write(_sv) except AttributeError: logger.warn( f'Skipping coverpoint generation for {_test_name} as ' f'there is no gen_covergroup method ') pass else: logger.critical(f'Skipped {_test_name} as this test is not ' f'created for the current DUT configuration ') logger.debug(f'Finished Generating Coverpoints for {module}') logger.info('****** Finished Generating Covergroups ******')
[docs]def validate_tests(modules, config_dict, work_dir, modules_dir): """ Parses the log returned from the DUT for finding if the tests were successful. The user should have created regular expressions for the patterns he's expecting to be seen in the log generated by the DUT. In addition to just the checking, it can also be set up to provide a report for every test for which the user tries to validate. """ uarch_dir = os.path.dirname(uatg.__file__) inp_yaml = config_dict logger.info('****** Validating Test results, Minimal log checking ******') if modules == ['all']: logger.debug(f'Checking {modules_dir} for modules') modules = list_of_modules(modules_dir) # del modules[-1] # Needed if list_of_modules returns 'all' along with other modules if work_dir: pass else: work_dir = os.path.abspath((os.path.join(uarch_dir, '../work/'))) _pass_ct = 0 _fail_ct = 0 _tot_ct = 1 for module in modules: module_dir = os.path.join(modules_dir, module) # module_tests_dir = os.path.join(module_dir, 'tests') work_tests_dir = os.path.join(work_dir, module) reports_dir = os.path.join(work_dir, 'reports', module) os.makedirs(reports_dir, exist_ok=True) try: module_params = inp_yaml[module] except KeyError: # logger.critical("The {0} module is not " # "in the dut config_file",format(module)) module_params = {} manager = PluginManager() manager.setPluginPlaces([module_dir]) manager.collectPlugins() logger.debug(f'Minimal Log Checking for {module}') for plugin in manager.getAllPlugins(): _name = (str(plugin.plugin_object).split(".", 1)) _test_name = ((_name[1].split(" ", 1))[0]) _check = plugin.plugin_object.execute(module_params) _log_file_path = os.path.join(work_tests_dir, _test_name, 'log') if _check: try: _result = plugin.plugin_object.check_log( _log_file_path, reports_dir) if _result: logger.info(f'{_tot_ct}. Minimal test: {_test_name} ' f'has passed.') _pass_ct += 1 _tot_ct += 1 else: logger.critical(f"{_tot_ct}. Minimal test: " f"{_test_name} has failed.") _fail_ct += 1 _tot_ct += 1 except FileNotFoundError: logger.error(f'Log for {_test_name} not found. Run the ' f'test on DUT and generate log or check ' f'the path.') else: logger.warn(f'No asm generated for {_test_name}. Skipping') logger.debug(f'Minimal log Checking for {module} complete') logger.info("Minimal Verification Results") logger.info("=" * 28) logger.info(f"Total Tests : {_tot_ct - 1}") if _tot_ct - 1: logger.info(f"Tests Passed : {_pass_ct} - " f"[{_pass_ct // (_tot_ct - 1)} %]") logger.warn(f"Tests Failed : {_fail_ct} - " f"[{100 * _fail_ct // (_tot_ct - 1)} %]") else: logger.warn("No tests were created") logger.info('****** Finished Validating Test results ******') join_yaml_reports(work_dir) logger.info('Joined Yaml reports')
[docs]def clean_dirs(work_dir, modules_dir): """ This function cleans the files generated by UATG. Presently it removes __pycache__, work_dir directory and also removes the '.yapsy plugins' files in the module's directories. """ uarch_dir = os.path.dirname(uatg.__file__) if work_dir: pass else: work_dir = os.path.abspath((os.path.join(uarch_dir, '../work/'))) module_dir = os.path.join(work_dir, '**') # module_tests_dir = os.path.join(module_dir, 'tests') logger.info('****** Cleaning ******') logger.debug(f'work_dir is {module_dir}') yapsy_dir = os.path.join(modules_dir, '**/*.yapsy-plugin') pycache_dir = os.path.join(modules_dir, '**/__pycache__') logger.debug(f'yapsy_dir is {yapsy_dir}') logger.debug(f'pycache_dir is {pycache_dir}') tf = glob.glob(module_dir) pf = glob.glob(pycache_dir) + glob.glob( os.path.join(uarch_dir, '__pycache__')) yf = glob.glob(yapsy_dir, recursive=True) logger.debug(f'removing {tf}, {yf} and {pf}') for element in tf + pf: if os.path.isdir(element): rmtree(element) else: os.remove(element) for element in yf: os.remove(element) logger.info("Generated Test files/folders removed")