From d8cf950f8e04964a9a062b0a820f338a3cf4ba4e Mon Sep 17 00:00:00 2001 From: Qinghao Shi Date: Fri, 23 Jul 2021 23:16:36 +0100 Subject: [PATCH 1/3] update IRIS API replace CADI API --- README.md | 87 +++++++------- fm_agent/configs/COVERAGE.conf | 12 +- fm_agent/configs/DEFAULT.conf | 15 --- fm_agent/configs/{FAST.conf => MPS2.conf} | 4 +- fm_agent/configs/MPS3.conf | 21 ++++ fm_agent/fm_agent.py | 60 ++++++---- fm_agent/fm_config.py | 98 +++++----------- fm_agent/mbedfm.py | 28 ++--- fm_agent/settings.json | 78 +++++-------- fm_agent/utils.py | 133 ++++++---------------- test/agent_test.py | 4 +- test/config_test.py | 16 --- test/utils_test.py | 35 ------ 13 files changed, 210 insertions(+), 381 deletions(-) delete mode 100644 fm_agent/configs/DEFAULT.conf rename fm_agent/configs/{FAST.conf => MPS2.conf} (85%) create mode 100644 fm_agent/configs/MPS3.conf delete mode 100644 test/utils_test.py diff --git a/README.md b/README.md index 555fd11..f765b7a 100644 --- a/README.md +++ b/README.md @@ -11,23 +11,19 @@ If user only need to run mbed OS applications or examples rather than Greentea t ## Requirements - 1. Make sure you have Arm Fast Models Libraries files installed to your host machines, as well as the Fast Models PyCADI. + 1. Make sure you have Arm Fast Models FVPs as well as the Fast Models Iris python modules installed. This Package is not including neither of them. Also a valid license to the Fastmodels could be required. >please referencing [Fast Models User Guide](https://developer.arm.com/docs/100965/latest) - 2. A valid Fast Models license been set up correctly. - ->please referencing [Fast Models User Guide](https://developer.arm.com/docs/100965/latest) - - 3. Greentea version 1.5.0 or later + 2. Use Greentea and mbedhtrun version 1.5.0 or later ``` -pip install mbed-greentea -U +# Check versions mbedgt --version -``` - 4. Htrun version 1.4.1 or later -``` -pip install mbed-host-tests -U mbedhtrun --version + +# Update mbedgt and mbedhtrun +pip install mbed-greentea -U +pip install mbed-host-tests -U ``` ## Download @@ -36,16 +32,15 @@ git clone https://github.com/ARMmbed/mbed-fastmodel-agent.git cd mbed-fastmodel-agent ``` -## Settings before installation +## Configurations before installation 1. Edit the configuration file `fm_agent\settings.json` -2. Change `model_lib_path` value in the `GLOBAL` section to your Fast Models installation folder (where contains all fastmodel libs). -3. Change `PyCADI_path` value to PyCADI folder, alternatively you can have `PVLIB_HOME` environment variable set on your host -4. Optional: edit individual models if necessary -5. Optional: add configs to models if necessary +3. Change `IRIS_path` value to IRIS Python folder +3. Edit individual models binarys file path +4. Optional: customize configs to models ## Installation ``` -python setup.py install +sudo python setup.py install ``` *NOTE. you will need to re-run the install command after you changed the "settings.json" or any config file* @@ -57,29 +52,24 @@ python setup.py install ``` you should be able to see a table like: ``` -+--------------+-----------------------------------------------+-------------+--------------+--------------+ -| MODEL NAME | MODEL LIB full path | CONFIG NAME | CONFIG FILE | AVAILABILITY | -+--------------+-----------------------------------------------+-------------+--------------+--------------+ -| | | DEFAULT | DEFAULT.conf | YES | -| FVP_MPS2_M0 | C:\work\model_libs\FVP_MPS2_Cortex-M0.dll | FAST | FAST.conf | YES | -| | | NETWORK | NETWORK.conf | YES | -+--------------+-----------------------------------------------+-------------+--------------+--------------+ -| | | DEFAULT | DEFAULT.conf | YES | -| FVP_MPS2_M0P | C:\work\model_libs\FVP_MPS2_Cortex-M0plus.dll | FAST | FAST.conf | YES | -| | | NETWORK | NETWORK.conf | YES | -+--------------+-----------------------------------------------+-------------+--------------+--------------+ -| | | DEFAULT | DEFAULT.conf | YES | -| FVP_MPS2_M3 | C:\work\model_libs\FVP_MPS2_Cortex-M3.dll | FAST | FAST.conf | YES | -| | | NETWORK | NETWORK.conf | YES | -+--------------+-----------------------------------------------+-------------+--------------+--------------+ -| | | DEFAULT | DEFAULT.conf | YES | -| FVP_MPS2_M4 | C:\work\model_libs\FVP_MPS2_Cortex-M4.dll | FAST | FAST.conf | YES | -| | | NETWORK | NETWORK.conf | YES | -+--------------+-----------------------------------------------+-------------+--------------+--------------+ -| | | DEFAULT | DEFAULT.conf | YES | -| FVP_MPS2_M7 | C:\work\model_libs\FVP_MPS2_Cortex-M7.dll | FAST | FAST.conf | YES | -| | | NETWORK | NETWORK.conf | YES | -+--------------+-----------------------------------------------+-------------+--------------+--------------+ ++--------------+-----------------------------------------------+-------------+---------------+--------------+ +| MODEL NAME | MODEL Binary Full Path | CONFIG NAME | CONFIG FILE | AVAILABILITY | ++--------------+-----------------------------------------------+-------------+---------------+--------------+ +| FVP_MPS2_M0 | C:\work\models\FVP_MPS2_Cortex-M0.exe | MPS2 | MPS2.conf | YES | +| | | COVERAGE | COVERAGE.conf | YES | ++--------------+-----------------------------------------------+-------------+---------------+--------------+ +| FVP_MPS2_M0P | C:\work\models\FVP_MPS2_Cortex-M0plus.exe | MPS2 | MPS2.conf | YES | +| | | COVERAGE | COVERAGE.conf | YES | ++--------------+-----------------------------------------------+-------------+---------------+--------------+ +| FVP_MPS2_M3 | C:\work\models\FVP_MPS2_Cortex-M3.exe | MPS2 | MPS2.conf | YES | +| | | COVERAGE | COVERAGE.conf | YES | ++--------------+-----------------------------------------------+-------------+---------------+--------------+ +| FVP_MPS2_M4 | C:\work\models\FVP_MPS2_Cortex-M4.exe | MPS2 | MPS2.conf | YES | +| | | COVERAGE | COVERAGE.conf | YES | ++--------------+-----------------------------------------------+-------------+---------------+--------------+ +| FVP_MPS2_M7 | C:\work\models\FVP_MPS2_Cortex-M7.exe | MPS2 | MPS2.conf | YES | +| | | COVERAGE | COVERAGE.conf | YES | ++--------------+-----------------------------------------------+-------------+---------------+--------------+ ``` ### Self test for the settings ``` @@ -92,13 +82,13 @@ This will try to launch every model in the above list to verify them, so will ta ``` mbedgt --fm : ``` ->e.g. `mbedgt --fm FVP_MPS2_M3:DEFAULT` +>e.g. `mbedgt --fm FVP_MPS2_M3:MPS2` ### run mbed test with htrun ``` - mbedhtrun --fm -m -f + mbedhtrun --fm -m -f <-e host_test_path> ``` ->e.g. `mbedhtrun --fm DEFAULT -m FVP_MPS2_M3 -f test.elf` +>e.g. `mbedhtrun -m FVP_MPS2_M3 --fm MPS2 -f test.elf -e ./host_tests` * : The name to fastmodel target supported in mbed os* @@ -108,9 +98,10 @@ This will try to launch every model in the above list to verify them, so will ta The mbed fastmodel_agent module allow user to configure each individual module via a config file. by default, 3 config files are provided: -* DEFAULT - default settings -* FAST - based on default turned off speed limit -* NETWORK - based on default enabled Ethernet +* MPS2 - default settings for MPS2 based platforms +* MPS3 - default settings for MPS3 based platforms +* COVERAGE - configuration for MPS2 Code Coverage Test + ## change config files @@ -123,9 +114,9 @@ The config files are standard Fast Models config file. for more detail about det Users are able to add their own customized config file to the `mbed-fastmodel-agent\fm_agent\configs` directory. -Then users need to edit `mbed-fastmodel-agent\fm_agent\settings.json` file either in `GLOBAL` section or individual models. +Then users need to edit `mbed-fastmodel-agent\fm_agent\settings.json` file either in `COMMON` section or individual models. -Key `configs_add` can be added for additional config files for each model, Or Key `config` can be added to overwrite `GLOBAL` config files. +Key `configs_add` can be added for additional config files for each model, Or Key `config` can be added to overwrite `COMMON` config files. ## Known limitations: 1. Fast Models normally have 3 or 4 serial terminal ports. But currently only one port is used at moment. diff --git a/fm_agent/configs/COVERAGE.conf b/fm_agent/configs/COVERAGE.conf index 6ea17f1..8a02f79 100644 --- a/fm_agent/configs/COVERAGE.conf +++ b/fm_agent/configs/COVERAGE.conf @@ -1,13 +1,13 @@ ## Turn off terminal1 and terminal2, keep terminal0 open -fvp_mps2.telnetterminal1.quiet=1 -fvp_mps2.telnetterminal2.quiet=1 +fvp_mps3.telnetterminal1.quiet=1 +fvp_mps3.telnetterminal2.quiet=1 ## Control the output format of the telnet terminal -fvp_mps2.telnetterminal0.mode=raw +mps3_board.telnetterminal0.mode=raw ## Suppress the telnet/xterm to be launched, so that model agent can talk to the port -fvp_mps2.telnetterminal0.start_telnet=0 +mps3_board.telnetterminal0.start_telnet=0 ## logging the UART output to a file @@ -17,5 +17,5 @@ fvp_mps2.telnetterminal0.start_telnet=0 fvp_mps2.mps2_visualisation.rate_limit-enable=0 ## Enable Ethernet Port -fvp_mps2.hostbridge.userNetworking=1 -fvp_mps2.smsc_91c111.enabled=1 +fvp_mps3.hostbridge.userNetworking=1 +fvp_mps3.smsc_91c111.enabled=1 diff --git a/fm_agent/configs/DEFAULT.conf b/fm_agent/configs/DEFAULT.conf deleted file mode 100644 index 613023e..0000000 --- a/fm_agent/configs/DEFAULT.conf +++ /dev/null @@ -1,15 +0,0 @@ -## Turn off terminal1 and terminal2, keep terminal0 open -fvp_mps2.telnetterminal1.quiet=1 -fvp_mps2.telnetterminal2.quiet=1 - - -## Control the output format of the telnet terminal -fvp_mps2.telnetterminal0.mode=raw - - -## Suppress the telnet/xterm to be launched, so that model agent can talk to the port -fvp_mps2.telnetterminal0.start_telnet=0 - - -## logging the UART output to a file -# fvp_mps2.UART0.out_file=out_file.txt diff --git a/fm_agent/configs/FAST.conf b/fm_agent/configs/MPS2.conf similarity index 85% rename from fm_agent/configs/FAST.conf rename to fm_agent/configs/MPS2.conf index 874691a..6e5467a 100644 --- a/fm_agent/configs/FAST.conf +++ b/fm_agent/configs/MPS2.conf @@ -1,6 +1,6 @@ ## Turn off terminal1 and terminal2, keep terminal0 open -fvp_mps2.telnetterminal1.quiet=1 -fvp_mps2.telnetterminal2.quiet=1 +## fvp_mps2.telnetterminal1.quiet=1 +## fvp_mps2.telnetterminal2.quiet=1 ## Control the output format of the telnet terminal diff --git a/fm_agent/configs/MPS3.conf b/fm_agent/configs/MPS3.conf new file mode 100644 index 0000000..89f40fb --- /dev/null +++ b/fm_agent/configs/MPS3.conf @@ -0,0 +1,21 @@ +## Turn off terminal1 terminal2 and termimal5, keep terminal0 open +mps3_board.telnetterminal1.quiet=1 +mps3_board.telnetterminal2.quiet=1 +mps3_board.telnetterminal5.quiet=1 + +## Control the output format of the telnet terminal +mps3_board.telnetterminal0.mode=raw +## Suppress the telnet/xterm to be launched, so that model agent can talk to the port +mps3_board.telnetterminal0.start_telnet=0 + + +## Turn the rate limite off, make FVP runs as fast as possiable +mps3_board.visualisation.rate_limit-enable=0 + + +## Turn on user Networking +mps3_board.smsc_91c111.enabled=1 +mps3_board.hostbridge.userNetworking=1 + +## Turn off GUI windows +# mps3_board.visualisation.disable-visualisation=1 diff --git a/fm_agent/fm_agent.py b/fm_agent/fm_agent.py index 6bc48b3..ef0421e 100644 --- a/fm_agent/fm_agent.py +++ b/fm_agent/fm_agent.py @@ -64,22 +64,30 @@ def setup_simulator(self, model_name, model_config): self.logger.prn_err("Please provided the name to a fastmodel!!") self.__guide() raise SimulatorError("fastmodel_name not provided!") - self.model_lib = self.configuration.get_model_lib(self.fastmodel_name) + self.model_binary = self.configuration.get_model_binary(self.fastmodel_name) - if not self.model_lib: - self.logger.prn_err("NO model_lib available for '%s'"% self.fastmodel_name) + if not self.model_binary: + self.logger.prn_err("NO model_binary available for '%s'"% self.fastmodel_name) self.__guide() raise SimulatorError("fastmodel '%s' not available" % (self.fastmodel_name)) config_dict = self.configuration.get_configs(self.fastmodel_name) if config_dict and self.config_name in config_dict: - self.model_params = self.configuration.parse_params_file(config_dict[self.config_name]) + config_file = config_dict[self.config_name] + self.model_config_file = os.path.join( os.path.dirname(__file__) ,"configs" , config_file ) + elif os.path.exists(os.path.join( os.getcwd(), self.config_name )): - self.model_params = self.configuration.parse_params_file(self.config_name,in_module=False) + self.model_config_file = os.path.join( os.getcwd() , config_name ) else: self.__guide() raise SimulatorError("No config %s avaliable for fastmodel %s" % (self.config_name,self.fastmodel_name)) + + self.model_terminal = self.configuration.get_model_terminal_comp(self.fastmodel_name) + + if not self.model_terminal: + self.logger.prn_err("NO terminal_compoment defined for '%s'"% self.fastmodel_name) + raise SimulatorError("fastmodel '%s' not defined terminal compoment" % (self.fastmodel_name)) def __connect_terminal(self): """ connect socket terminal to a launched fastmodel""" @@ -105,13 +113,16 @@ def is_simulator_alive(self): def start_simulator(self): """ launch given fastmodel with configs """ if check_import(): - import fm.debug - self.model = fm.debug.LibraryModel(self.model_lib, self.model_params) + import iris.debug + proc, IRIS_port, outs = launch_FVP_IRIS(self.model_binary, self.model_config_file) + print(outs) + self.model = iris.debug.NetworkModel('localhost',IRIS_port) # check which host socket port is used for terminal0 - terminal = self.model.get_target('fvp_mps2.telnetterminal0') - self.port = terminal.read_register('Port') + terminal = self.model.get_target(self.model_terminal) + self.port = terminal.read_register('Default.Port') self.host = "localhost" self.image = None + return True else: raise SimulatorError("fastmodel product was NOT installed correctly") @@ -151,11 +162,16 @@ def reset_simulator(self): self.__closeConnection() self.model.release(shutdown=True) time.sleep(1) - import fm.debug - self.model = fm.debug.LibraryModel(self.model_lib, self.model_params) + import iris.debug + + proc, IRIS_port, outs = launch_FVP_IRIS(self.model_binary, self.model_config_file) + if IRIS_port==0: + print(outs) + return False + self.model = iris.debug.NetworkModel('localhost',IRIS_port) # check which host socket port is used for terminal0 - terminal = self.model.get_target('fvp_mps2.telnetterminal0') - self.port = terminal.read_register('Port') + terminal = self.model.get_target(self.model_terminal) + self.port = terminal.read_register('Default.Port') cpu = self.model.get_cpus()[0] if self.image: cpu.load_application(self.image) @@ -172,12 +188,12 @@ def read(self): if not self.__socketConnected(): return None - data = str() + data = bytearray() read_stop = False while not read_stop: try: - char='' + char=bytearray() char = self.socket.recv(1) except socket.timeout as eto: read_stop=True @@ -205,7 +221,7 @@ def write(self, payload, log=False): try: for char in payload: - self.socket.sendall(char) + self.socket.sendall(char.encode()) time.sleep(0.01) if log: self.logger.prn_txd(payload) @@ -325,16 +341,12 @@ def list_avaliable_models(self): """ return a dictionary of models and configs """ return self.configuration.get_all_configs() - def list_model_lib(self, model_name): - """ return model lib full path of give model_name """ - return self.configuration.get_model_lib(model_name) + def list_model_binary(self, model_name): + """ return model binary full path of give model_name """ + return self.configuration.get_model_binary(model_name) def check_config_exist(self,filename): """ return the presents of give config name """ - filepath = os.path.join( os.path.dirname(__file__), "configs" , filename ) - - return os.path.exists(filepath) - - + return os.path.exists(filepath) \ No newline at end of file diff --git a/fm_agent/fm_config.py b/fm_agent/fm_config.py index f8b821f..bdd727e 100644 --- a/fm_agent/fm_config.py +++ b/fm_agent/fm_config.py @@ -18,9 +18,6 @@ import json import os.path -from .utils import check_host_os -from .utils import remove_comments -from .utils import boolean_filter from .utils import SimulatorError class FastmodelConfig(): @@ -35,7 +32,6 @@ def __init__(self): with open(settings_json_file,"r") as config_json: self.json_configs = json.load(config_json) - self.os = check_host_os(); def get_all_configs (self): """ search every config for all the models in SETTINGS_FILE @@ -43,40 +39,46 @@ def get_all_configs (self): """ all_config_dict={} for model in self.json_configs.keys(): - if model != "GLOBAL": + if model != "COMMON": all_config_dict[model]=self.get_configs(model) return all_config_dict - def get_PyCADI_path(self): - """ get the PyCADI path from the config file - @return PyCADI path if setting exist + def get_IRIS_path(self): + """ get the IRIS path from the config file + @return IRIS path if setting exist @return None if not exist """ - if "PyCADI_path" in self.json_configs["GLOBAL"][self.os]: - return self.json_configs["GLOBAL"][self.os]["PyCADI_path"] + if "IRIS_path" in self.json_configs["COMMON"]: + return self.json_configs["COMMON"]["IRIS_path"] else: return None - def get_model_lib(self,model_name): - """ get the model lib path and name from the config file - @return full name and path to the model_lib + def get_model_binary(self,model_name): + """ get the model binary path and name from the config file + @return full name and path to the model_binary @return None if not exist """ if model_name not in self.json_configs: return None - if "model_lib" not in self.json_configs[model_name][self.os]: + if "model_binary" not in self.json_configs[model_name]: return None + + return self.json_configs[model_name]["model_binary"] - model_lib_name = self.json_configs[model_name][self.os]["model_lib"] + def get_model_terminal_comp(self,model_name): + """ get the model terminal compoment name from the config file + @return full name to model terminal compoment + @return None if not exist + """ + if model_name not in self.json_configs: + return None - if "model_lib_path" in self.json_configs[model_name][self.os]: - local_path = self.json_configs[model_name][self.os]["model_lib_path"] - return str(os.path.join( local_path , model_lib_name )) - else: - global_path = self.json_configs["GLOBAL"][self.os]["model_lib_path"] - return str(os.path.join( global_path , model_lib_name )) + if "terminal_component" not in self.json_configs[model_name]: + return None + + return self.json_configs[model_name]["terminal_component"] def get_configs (self,model_name): """ Search for configs with given model @@ -86,55 +88,13 @@ def get_configs (self,model_name): if model_name not in self.json_configs: return None - if "configs" in self.json_configs[model_name][self.os]: - local_configs = self.json_configs[model_name][self.os]["configs"].copy() + if "configs" in self.json_configs[model_name]: + local_configs = self.json_configs[model_name]["configs"].copy() return local_configs - elif "configs_add" in self.json_configs[model_name][self.os]: - global_configs = self.json_configs["GLOBAL"][self.os]["configs"].copy() - addtion_configs = self.json_configs[model_name][self.os]["configs_add"].copy() + elif "configs_add" in self.json_configs[model_name]: + global_configs = self.json_configs["COMMON"]["configs"].copy() + addtion_configs = self.json_configs[model_name]["configs_add"].copy() return dict(global_configs,**addtion_configs) else: - global_configs = self.json_configs["GLOBAL"][self.os]["configs"].copy() + global_configs = self.json_configs["COMMON"]["configs"].copy() return global_configs - - def parse_params_file (self, config_file , in_module=True): - """ read fastmodel parameters from given config_file - @param config_file need to be file name only - @param in_module default is True, means the config_file inside module folder - @param if in_module set to False, will looking for config_file in pwd - @return if config_file read failed, function will throw SimulatorError - @return if config_file format is wrong, function will throw SimulatorError - @return if config_file been parsed successfully, will return a dictionary of parameters - """ - if in_module: - filepath = os.path.join( os.path.dirname(__file__) ,"configs" , config_file ) - else: - filepath = os.path.join( os.getcwd() , config_file ) - - if not os.path.exists(filepath): - raise SimulatorError("model config file not exit: %s" % filepath) - return None - - params_dict={} - with open(filepath,'r') as CONF_file: - param_data = CONF_file.readlines() - for line in param_data: - line = remove_comments(line) - if line: - if line.count("=")==0: - raise SimulatorError("Wrong format in config %s,\nline %s should match format key=values" % (filepath,line)) - return None - elif line.count("=")>1: - raise SimulatorError("Wrong format in config %s,\nline %s having more than one '='" % (filepath,line)) - return None - elif line.startswith("="): - raise SimulatorError("Wrong format in config %s,\nline %s should match format key=values" % (filepath,line)) - return None - elif line.endswith("="): - raise SimulatorError("Wrong format in config %s,\nline %s should match format key=values" % (filepath,line)) - return None - else: - param_key,param_value = line.split("=") - params_dict[param_key.strip()] = boolean_filter(param_value) - - return params_dict diff --git a/fm_agent/mbedfm.py b/fm_agent/mbedfm.py index 4544f33..06d528b 100644 --- a/fm_agent/mbedfm.py +++ b/fm_agent/mbedfm.py @@ -18,9 +18,9 @@ import sys import os -from fm_agent import FastmodelAgent -from fm_agent import SimulatorError -from fm_agent import check_import +from .fm_agent import FastmodelAgent +from .fm_agent import SimulatorError +from .utils import check_import from prettytable import PrettyTable import argparse @@ -35,11 +35,11 @@ def print_version(): def print_models(): print(list_fastmodels()) - print("Import PyCADI Test ... {}".format("PASSED" if check_import() else "FAILED")) + print("Import IRIS Test ... {}".format("PASSED" if check_import() else "FAILED")) def self_test(): print(list_fastmodels(check_models=True)) - print("Import PyCADI Test ... {}".format("PASSED" if check_import() else "FAILED")) + print("Import IRIS Test ... {}".format("PASSED" if check_import() else "FAILED")) def list_fastmodels(check_models=False): """! List all models and configs in fm_agent""" @@ -47,7 +47,7 @@ def list_fastmodels(check_models=False): resource = FastmodelAgent() model_dict = resource.list_avaliable_models() - columns = ['MODEL NAME', "MODEL LIB full path", 'CONFIG NAME' , 'CONFIG FILE', 'AVAILABILITY'] + columns = ['MODEL NAME', "MODEL Binary Full Path", 'CONFIG NAME' , 'CONFIG FILE', 'AVAILABILITY'] if check_models: columns.append('SELF TEST') @@ -58,7 +58,7 @@ def list_fastmodels(check_models=False): pt.align[col] = 'l' for model_name, configs in sorted(model_dict.items()): - lib_path = resource.list_model_lib(model_name) + binary_path = resource.list_model_binary(model_name) c_names_cell=[] c_files_cell=[] @@ -70,12 +70,12 @@ def list_fastmodels(check_models=False): c_names_cell.append(config_name) c_files_cell.append(config_file) - for file in c_files_cell: - if not os.path.exists(lib_path): - c_avail_cell.append("NO 'MODEL LIB' NOT EXIST") + + if not os.path.exists(binary_path): + c_avail_cell.append("NO 'MODEL BINARY' NOT EXIST") if check_models: c_test_cell.append("SKIPPED") - elif resource.check_config_exist(file): + elif resource.check_config_exist(config_file): c_avail_cell.append("YES") if check_models: try: @@ -92,13 +92,13 @@ def list_fastmodels(check_models=False): c_test_cell.append("SKIPPED") MAX_WIDTH = 60 - lib_path_cell = [lib_path[i:i+MAX_WIDTH] for i in range(0, len(lib_path), MAX_WIDTH)] + binary_path_cell = [binary_path[i:i+MAX_WIDTH] for i in range(0, len(binary_path), MAX_WIDTH)] padding_lines_col1 = "\n" * ((len(c_names_cell)-1) // 2) - padding_lines_col2 = "\n" * ((len(c_names_cell)-len(lib_path_cell)) // 2) + padding_lines_col2 = "\n" * ((len(c_names_cell)-len(binary_path_cell)) // 2) row_list = [padding_lines_col1+model_name, - padding_lines_col2+"\n".join(lib_path_cell), + padding_lines_col2+"\n".join(binary_path_cell), "\n".join(c_names_cell), "\n".join(c_files_cell), "\n".join(c_avail_cell)] diff --git a/fm_agent/settings.json b/fm_agent/settings.json index 4f929bc..502f30e 100644 --- a/fm_agent/settings.json +++ b/fm_agent/settings.json @@ -1,63 +1,43 @@ { - "GLOBAL": { - "Windows":{ - "PyCADI_path": "C:\\work\\model_libs\\python27", - "model_lib_path": "C:\\work\\model_libs", - "configs": { - "DEFAULT": "DEFAULT.conf", - "NETWORK": "NETWORK.conf", - "COVERAGE":"COVERAGE.conf" - } - }, - "Linux":{ - "PyCADI_path": "/work/model_libs/python2.7/", - "model_lib_path": "/work/model_libs", - "configs": { - "DEFAULT": "DEFAULT.conf", - "FAST": "FAST.conf", - "NETWORK": "NETWORK.conf", - "COVERAGE":"COVERAGE.conf" - } + "COMMON": { + "IRIS_path": "/home/qinshi01/FVP_Corstone_SSE-300_Ethos-U55/Iris/Python/", + "configs": { + "MPS2": "MPS2.conf", + "NETWORK": "NETWORK.conf" } }, - "FVP_MPS2_M0": { - "Windows":{ - "model_lib": "FVP_MPS2_Cortex-M0.dll" - }, - "Linux":{ - "model_lib": "FVP_MPS2_Cortex-M0.so" + "FVP_CS300_U55": { + "model_binary": "/home/qinshi01/FVP_Corstone_SSE-300_Ethos-U55/models/Linux64_GCC-6.4/FVP_Corstone_SSE-300_Ethos-U55", + "terminal_component": "component.FVP_MPS3_Corstone_SSE_300.mps3_board.telnetterminal0", + "configs": { + "MPS3": "MPS3.conf" } + + }, + + "FVP_MPS2_M0": { + "model_binary": "/home/qinshi01/FM_11.15/FVP_MPS2_Cortex-M0", + "terminal_component": "component.FVP_MPS2_Cortex_M3.fvp_mps2.telnetterminal0" }, + "FVP_MPS2_M0P": { - "Windows":{ - "model_lib": "FVP_MPS2_Cortex-M0plus.dll" - }, - "Linux":{ - "model_lib": "FVP_MPS2_Cortex-M0plus.so" - } + + "model_binary": "/home/qinshi01/FM_11.15/FVP_MPS2_Cortex-M0plus", + "terminal_component": "component.FVP_MPS2_Cortex_M3.fvp_mps2.telnetterminal0" }, + "FVP_MPS2_M3": { - "Windows":{ - "model_lib": "FVP_MPS2_Cortex-M3.dll" - }, - "Linux":{ - "model_lib": "FVP_MPS2_Cortex-M3.so" - } + "model_binary": "/home/qinshi01/FM_11.15/FVP_MPS2_Cortex-M3", + "terminal_component": "component.FVP_MPS2_Cortex_M3.fvp_mps2.telnetterminal0" }, + "FVP_MPS2_M4": { - "Windows":{ - "model_lib": "FVP_MPS2_Cortex-M4.dll" - }, - "Linux":{ - "model_lib": "FVP_MPS2_Cortex-M4.so" - } + "model_binary": "/home/qinshi01/FM_11.15/FVP_MPS2_Cortex-M4", + "terminal_component": "component.FVP_MPS2_Cortex_M3.fvp_mps2.telnetterminal0" }, + "FVP_MPS2_M7": { - "Windows":{ - "model_lib": "FVP_MPS2_Cortex-M7.dll" - }, - "Linux":{ - "model_lib": "FVP_MPS2_Cortex-M7.so" - } + "model_binary": "/home/qinshi01/FM_11.15/FVP_MPS2_Cortex-M7", + "terminal_component": "component.FVP_MPS2_Cortex_M3.fvp_mps2.telnetterminal0" } } diff --git a/fm_agent/utils.py b/fm_agent/utils.py index 45308dc..1c68eca 100644 --- a/fm_agent/utils.py +++ b/fm_agent/utils.py @@ -23,8 +23,8 @@ import time import socket import logging -import subprocess from functools import partial +from subprocess import Popen, PIPE, STDOUT, TimeoutExpired class SimulatorError(Exception): """ @@ -53,46 +53,33 @@ def __prn_log(self, logger_level, text, timestamp=None): self.prn_txd = partial(__prn_log, self, 'TXD') self.prn_rxd = partial(__prn_log, self, 'RXD') -def get_PyCADI_path(self): - """ get the PyCADI path from the config file - @return PyCADI path if setting exist +def get_IRIS_path(self): + """ get the IRIS path from the config file + @return IRIS path if setting exist @return None if not exist """ - if "PyCADI_path" in self.json_configs["GLOBAL"]: - return self.json_configs["GLOBAL"][self.os]["PyCADI_path"] + if "IRIS_path" in self.json_configs["COMMON"]: + return self.json_configs["COMMON"]["IRIS_path"] else: return None def check_import(): - """ Append PVLIB_HOME to PATH, so import PyCADI fm.debug can be imported """ + """ try PyIRIS API iris.debug can be imported """ warning_msgs = [] from .fm_config import FastmodelConfig config = FastmodelConfig() - fm_pycadi_path = config.get_PyCADI_path() - if fm_pycadi_path: - if os.path.exists(fm_pycadi_path): - sys.path.append(fm_pycadi_path) + fm_IRIS_path = config.get_IRIS_path() + if fm_IRIS_path: + if os.path.exists(fm_IRIS_path): + sys.path.append(fm_IRIS_path) else: - warning_msgs.append("Warning: Could not locate PyCADI_path '%s'" % fm_pycadi_path) + warning_msgs.append("Warning: Could not locate IRIS_path '%s'" % fm_IRIS_path) else: - warning_msgs.append("Warning: PyCADI_path not set in settings.json") - - if 'PVLIB_HOME' in os.environ: - #FastModels PyCADI have different folder on different host OS - fm_pycadi_path1 = os.path.join(os.environ['PVLIB_HOME'], 'lib', 'python27') - fm_pycadi_path2 = os.path.join(os.environ['PVLIB_HOME'], 'lib', 'python2.7') - if os.path.exists(fm_pycadi_path1): - sys.path.append(fm_pycadi_path1) - elif os.path.exists(fm_pycadi_path2): - sys.path.append(fm_pycadi_path2) - else: - warning_msgs.append("Warning: Could not locate PyCADI in PVLIB_HOME/lib/python27") - else: - warning_msgs.append("Warning: 'PVLIB_HOME' environment variable not been set.") + warning_msgs.append("Warning: IRIS_path not set in settings.json") try: - import fm.debug + import iris.debug except ImportError as e: for warning in warning_msgs: print(warning) @@ -100,80 +87,6 @@ def check_import(): return False else: return True - -def redirect_file(): - time.sleep(1) - sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) - os.dup2(tee.stdin.fileno(), sys.stdout.fileno()) - -def get_log(): - data=[] - sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) - os.dup2(SAVE, sys.stdout.fileno()) - - tee.terminate() - - with open (_TEMP_STDOUT,"r") as f: - contents = f.readlines() - for line in contents: - line = line.strip() - if line == "": - pass - else: - data.append(line) - return data - -def check_host_os(): - """ check and return the type of host operating system """ - if platform.system().startswith("Windows"): - return"Windows" - elif platform.system().startswith("Linux"): - return "Linux" - else: - return "UNKNOWN" - -def remove_comments(line): - """remove # comments from given line """ - i = line.find("#") - if i >= 0: - line = line[:i] - return line.strip() - -def strip_quotes(value): - """strip both single or double quotes""" - value = value.strip() - if "\"" in value: - return value.strip("\"") - elif "\'" in value: - return value.strip("\'") - else: - return value - -def boolean_filter(value): - - """ try to determine if give string match boolean type - @return boolean if the value matches - @return the original value if not match - """ - - value = strip_quotes(value) - - if value in ["TRUE","True","true","1"]: - return True - elif value in ["FALSE","False","false","0"]: - return False - else: - return value - -def find_free_port(): - """try to determine a free random port""" - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.bind(('localhost', 0)) - addr, port = s.getsockname() - s.close() - return port - - """ the following function mainly for coverage mode """ def read_symbol(image): """this function reads images symbol to a global variable""" @@ -214,3 +127,21 @@ def remove_gcda(rootdir="."): for file in files: if file.endswith(".gcda"): os.remove(os.path.join(root, file)) + +def launch_FVP_IRIS(model_exec, config_file='', lines_out=6): + """Launch FVP with IRIS Server listening""" + cmd_line = [model_exec, '-I', '-p'] + if config_file: + cmd_line.extend(['-f' , config_file]) + fm_proc = Popen(cmd_line,stdout=PIPE,stderr=STDOUT) + + stdout='' + port = 0 + + for num in range(lines_out): + line = fm_proc.stdout.readline().decode() + if line.startswith("Iris server started listening to port"): + port = int(line[-5:]) + stdout += line + + return (fm_proc, port, stdout) diff --git a/test/agent_test.py b/test/agent_test.py index 294ddb6..1c636f0 100644 --- a/test/agent_test.py +++ b/test/agent_test.py @@ -4,7 +4,7 @@ class TestFastmodelAgent(TestCase): def test_check_configs_true(self): - s = fm_agent.create().check_config_exist("DEFAULT.conf") + s = fm_agent.create().check_config_exist("MPS2.conf") self.assertTrue(s) def test_check_configs_false(self): s = fm_agent.create().check_config_exist("THISFILENOTEXIST.conf") @@ -15,7 +15,7 @@ def test_init_without_run(self): self.assertFalse(s.config_name) self.assertTrue(s.configuration) def test_init_with_run(self): - s = fm_agent.create("FVP_MPS2_M3","DEFAULT") + s = fm_agent.create("FVP_MPS2_M3","MPS2") self.assertTrue(s.fastmodel_name) self.assertTrue(s.config_name) self.assertTrue(s.configuration) \ No newline at end of file diff --git a/test/config_test.py b/test/config_test.py index 8f57e7e..086f6c2 100644 --- a/test/config_test.py +++ b/test/config_test.py @@ -6,22 +6,6 @@ class TestFastmodelConfig(TestCase): def test_Setting_File(self): self.assertTrue(FastmodelConfig.SETTINGS_FILE,"settings.json") - def test_parse_params_file_failed(self): - c=FastmodelConfig() - - try: - c.parse_params_file("FILE_NOT_EXIST") - except SimulatorError as e: - pass - else: - self.fail("failed to catch the exception") - - def test_parse_params_file(self): - c=FastmodelConfig() - try: - c.parse_params_file("DEFAULT.conf") - except SimulatorError as e: - self.fail("caught an SimulatorError exception") def test_get_configs_none(self): c=FastmodelConfig() diff --git a/test/utils_test.py b/test/utils_test.py deleted file mode 100644 index e6761af..0000000 --- a/test/utils_test.py +++ /dev/null @@ -1,35 +0,0 @@ -from unittest import TestCase - - -from fm_agent.utils import SimulatorError -from fm_agent.utils import check_host_os -from fm_agent.utils import remove_comments -from fm_agent.utils import strip_quotes -from fm_agent.utils import boolean_filter - - -class Testutils(TestCase): - def test_remove_comments(self): - self.assertEqual(remove_comments("#"),"") - self.assertEqual(remove_comments("#"),"") - self.assertEqual(remove_comments("#single hash"),"") - self.assertEqual(remove_comments("##double hash"),"") - self.assertEqual(remove_comments("text#comment"),"text") - self.assertEqual(remove_comments("text"),"text") - - def test_remove_quotes(self): - self.assertEqual(strip_quotes("no quote"),"no quote") - self.assertEqual(strip_quotes("'single quote'"),"single quote") - self.assertEqual(strip_quotes("\"double quote\""),"double quote") - - def test_boolean_filter(self): - self.assertEqual(boolean_filter("TRUE"),True) - self.assertEqual(boolean_filter("True"),True) - self.assertEqual(boolean_filter("true"),True) - self.assertEqual(boolean_filter("1"),True) - self.assertEqual(boolean_filter("FALSE"),False) - self.assertEqual(boolean_filter("False"),False) - self.assertEqual(boolean_filter("false"),False) - self.assertEqual(boolean_filter("0"),False) - self.assertEqual(boolean_filter("NOT"),"NOT") - self.assertEqual(boolean_filter("none"),"none") \ No newline at end of file From 79fa2bf0121c32e580223507210f14955718114a Mon Sep 17 00:00:00 2001 From: Qinghao Shi Date: Mon, 26 Jul 2021 18:58:51 +0100 Subject: [PATCH 2/3] Add GHA CI Test --- .github/ci_test_settings.json | 12 ++++++++++++ .github/workflows/PR-Build.yml | 29 +++++++++++++++++++++++++++++ fm_agent/mbedfm.py | 16 +++++++++------- fm_agent/settings.json | 14 +++++++------- 4 files changed, 57 insertions(+), 14 deletions(-) create mode 100644 .github/ci_test_settings.json create mode 100644 .github/workflows/PR-Build.yml diff --git a/.github/ci_test_settings.json b/.github/ci_test_settings.json new file mode 100644 index 0000000..9323dde --- /dev/null +++ b/.github/ci_test_settings.json @@ -0,0 +1,12 @@ +{ + "COMMON": { + "IRIS_path": "/opt/FVP_Corstone_SSE-300_Ethos-U55/Iris/Python/" + }, + "FVP_CS300_U55": { + "model_binary": "/opt/FVP_Corstone_SSE-300_Ethos-U55/models/Linux64_GCC-6.4//FVP_Corstone_SSE-300_Ethos-U55", + "terminal_component": "component.FVP_MPS3_Corstone_SSE_300.mps3_board.telnetterminal0", + "configs": { + "MPS3": "MPS3.conf" + } + } +} diff --git a/.github/workflows/PR-Build.yml b/.github/workflows/PR-Build.yml new file mode 100644 index 0000000..fbf6d0e --- /dev/null +++ b/.github/workflows/PR-Build.yml @@ -0,0 +1,29 @@ +# Pull-Request Test +name: PR-Check Build and Run + +on: + # Triggers the workflow on pull request + pull_request: + +jobs: + + Corstone300: + runs-on: ubuntu-latest + # Run this this job inside container + container: mbedos/mbed-os-env-fm:corstone300 + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Unit Test + run: python setup.py test + + - name: Install Test + run: | + rm fm_agent/settings.json + cp .github/ci_test_settings.json fm_agent/settings.json + python setup.py install + + - name: Run Self Test + run: mbedfm --self-test \ No newline at end of file diff --git a/fm_agent/mbedfm.py b/fm_agent/mbedfm.py index 06d528b..a16eeb1 100644 --- a/fm_agent/mbedfm.py +++ b/fm_agent/mbedfm.py @@ -35,11 +35,15 @@ def print_version(): def print_models(): print(list_fastmodels()) - print("Import IRIS Test ... {}".format("PASSED" if check_import() else "FAILED")) + result_pass = check_import() + print("Import IRIS Test ... {}".format("PASSED" if result_pass else "FAILED")) + return result_pass def self_test(): print(list_fastmodels(check_models=True)) - print("Import IRIS Test ... {}".format("PASSED" if check_import() else "FAILED")) + result_pass = check_import() + print("Import IRIS Test ... {}".format("PASSED" if result_pass else "FAILED")) + return result_pass def list_fastmodels(check_models=False): """! List all models and configs in fm_agent""" @@ -124,8 +128,6 @@ def cli_parser(in_args): def main(): args = cli_parser(sys.argv[1:]) - ret_code = args.command() - if not ret_code: - ret_code = 0 - sys.exit(ret_code) - + success = args.command() + if not success: + sys.exit(1) diff --git a/fm_agent/settings.json b/fm_agent/settings.json index 502f30e..c2fb095 100644 --- a/fm_agent/settings.json +++ b/fm_agent/settings.json @@ -1,13 +1,13 @@ { "COMMON": { - "IRIS_path": "/home/qinshi01/FVP_Corstone_SSE-300_Ethos-U55/Iris/Python/", + "IRIS_path": "/opt/FVP_Corstone_SSE-300_Ethos-U55/Iris/Python/", "configs": { "MPS2": "MPS2.conf", "NETWORK": "NETWORK.conf" } }, "FVP_CS300_U55": { - "model_binary": "/home/qinshi01/FVP_Corstone_SSE-300_Ethos-U55/models/Linux64_GCC-6.4/FVP_Corstone_SSE-300_Ethos-U55", + "model_binary": "/opt/FVP_Corstone_SSE-300_Ethos-U55/models/Linux64_GCC-6.4//FVP_Corstone_SSE-300_Ethos-U55", "terminal_component": "component.FVP_MPS3_Corstone_SSE_300.mps3_board.telnetterminal0", "configs": { "MPS3": "MPS3.conf" @@ -16,28 +16,28 @@ }, "FVP_MPS2_M0": { - "model_binary": "/home/qinshi01/FM_11.15/FVP_MPS2_Cortex-M0", + "model_binary": "/opt/FM_FVP/FVP_MPS2_Cortex-M0", "terminal_component": "component.FVP_MPS2_Cortex_M3.fvp_mps2.telnetterminal0" }, "FVP_MPS2_M0P": { - "model_binary": "/home/qinshi01/FM_11.15/FVP_MPS2_Cortex-M0plus", + "model_binary": "/opt/FM_FVP/FVP_MPS2_Cortex-M0plus", "terminal_component": "component.FVP_MPS2_Cortex_M3.fvp_mps2.telnetterminal0" }, "FVP_MPS2_M3": { - "model_binary": "/home/qinshi01/FM_11.15/FVP_MPS2_Cortex-M3", + "model_binary": "/opt/FM_FVP/FVP_MPS2_Cortex-M3", "terminal_component": "component.FVP_MPS2_Cortex_M3.fvp_mps2.telnetterminal0" }, "FVP_MPS2_M4": { - "model_binary": "/home/qinshi01/FM_11.15/FVP_MPS2_Cortex-M4", + "model_binary": "/opt/FM_FVP/FVP_MPS2_Cortex-M4", "terminal_component": "component.FVP_MPS2_Cortex_M3.fvp_mps2.telnetterminal0" }, "FVP_MPS2_M7": { - "model_binary": "/home/qinshi01/FM_11.15/FVP_MPS2_Cortex-M7", + "model_binary": "/opt/FM_FVP/FVP_MPS2_Cortex-M7", "terminal_component": "component.FVP_MPS2_Cortex_M3.fvp_mps2.telnetterminal0" } } From 4b9bcabbea89bc92e5c9de982da490dbe13989eb Mon Sep 17 00:00:00 2001 From: Qinghao Shi Date: Mon, 26 Jul 2021 19:10:43 +0100 Subject: [PATCH 3/3] update config file --- fm_agent/configs/COVERAGE.conf | 15 +++++++++------ fm_agent/configs/MPS2.conf | 9 ++++++--- fm_agent/configs/MPS3.conf | 2 +- fm_agent/configs/NETWORK.conf | 19 ------------------- fm_agent/settings.json | 3 +-- 5 files changed, 17 insertions(+), 31 deletions(-) delete mode 100644 fm_agent/configs/NETWORK.conf diff --git a/fm_agent/configs/COVERAGE.conf b/fm_agent/configs/COVERAGE.conf index 8a02f79..3046432 100644 --- a/fm_agent/configs/COVERAGE.conf +++ b/fm_agent/configs/COVERAGE.conf @@ -1,13 +1,13 @@ ## Turn off terminal1 and terminal2, keep terminal0 open -fvp_mps3.telnetterminal1.quiet=1 -fvp_mps3.telnetterminal2.quiet=1 +fvp_mps2.telnetterminal1.quiet=1 +fvp_mps2.telnetterminal2.quiet=1 ## Control the output format of the telnet terminal -mps3_board.telnetterminal0.mode=raw +fvp_mps2.telnetterminal0.mode=raw ## Suppress the telnet/xterm to be launched, so that model agent can talk to the port -mps3_board.telnetterminal0.start_telnet=0 +fvp_mps2.telnetterminal0.start_telnet=0 ## logging the UART output to a file @@ -17,5 +17,8 @@ mps3_board.telnetterminal0.start_telnet=0 fvp_mps2.mps2_visualisation.rate_limit-enable=0 ## Enable Ethernet Port -fvp_mps3.hostbridge.userNetworking=1 -fvp_mps3.smsc_91c111.enabled=1 +fvp_mps2.hostbridge.userNetworking=1 +fvp_mps2.smsc_91c111.enabled=1 + +## Turn off GUI windows +fvp_mps2.mps2_visualisation.disable-visualisation=1 diff --git a/fm_agent/configs/MPS2.conf b/fm_agent/configs/MPS2.conf index 6e5467a..6ea17f1 100644 --- a/fm_agent/configs/MPS2.conf +++ b/fm_agent/configs/MPS2.conf @@ -1,7 +1,6 @@ ## Turn off terminal1 and terminal2, keep terminal0 open -## fvp_mps2.telnetterminal1.quiet=1 -## fvp_mps2.telnetterminal2.quiet=1 - +fvp_mps2.telnetterminal1.quiet=1 +fvp_mps2.telnetterminal2.quiet=1 ## Control the output format of the telnet terminal fvp_mps2.telnetterminal0.mode=raw @@ -16,3 +15,7 @@ fvp_mps2.telnetterminal0.start_telnet=0 ## turn the rate limite off fvp_mps2.mps2_visualisation.rate_limit-enable=0 + +## Enable Ethernet Port +fvp_mps2.hostbridge.userNetworking=1 +fvp_mps2.smsc_91c111.enabled=1 diff --git a/fm_agent/configs/MPS3.conf b/fm_agent/configs/MPS3.conf index 89f40fb..bc948c1 100644 --- a/fm_agent/configs/MPS3.conf +++ b/fm_agent/configs/MPS3.conf @@ -18,4 +18,4 @@ mps3_board.smsc_91c111.enabled=1 mps3_board.hostbridge.userNetworking=1 ## Turn off GUI windows -# mps3_board.visualisation.disable-visualisation=1 +mps3_board.visualisation.disable-visualisation=1 diff --git a/fm_agent/configs/NETWORK.conf b/fm_agent/configs/NETWORK.conf deleted file mode 100644 index 0c6bf03..0000000 --- a/fm_agent/configs/NETWORK.conf +++ /dev/null @@ -1,19 +0,0 @@ -## Turn off terminal1 and terminal2, keep terminal0 open -fvp_mps2.telnetterminal1.quiet=1 -fvp_mps2.telnetterminal2.quiet=1 - - -## Control the output format of the telnet terminal -fvp_mps2.telnetterminal0.mode=raw - - -## Suppress the telnet/xterm to be launched, so that model agent can talk to the port -fvp_mps2.telnetterminal0.start_telnet=0 - - -## Logging the UART output to a file -# fvp_mps2.UART0.out_file=out_file.txt - -## Enable Ethernet Port -fvp_mps2.hostbridge.userNetworking=1 -fvp_mps2.smsc_91c111.enabled=1 diff --git a/fm_agent/settings.json b/fm_agent/settings.json index c2fb095..74fab7f 100644 --- a/fm_agent/settings.json +++ b/fm_agent/settings.json @@ -3,7 +3,7 @@ "IRIS_path": "/opt/FVP_Corstone_SSE-300_Ethos-U55/Iris/Python/", "configs": { "MPS2": "MPS2.conf", - "NETWORK": "NETWORK.conf" + "COVERAGE": "COVERAGE.conf" } }, "FVP_CS300_U55": { @@ -12,7 +12,6 @@ "configs": { "MPS3": "MPS3.conf" } - }, "FVP_MPS2_M0": {