diff --git a/README.md b/README.md index 9009406..555fd11 100644 --- a/README.md +++ b/README.md @@ -4,17 +4,31 @@ mbed-fastmodel-agent is a python module for Mbed OS GreenTea testing framework using FastModels FVP (Fixed Virtual Platforms). -This module only designed to work with [Greentea](https://github.com/ARMmbed/greentea) and [Htrun](https://github.com/ARMmbed/htrun), +This module only designed to work with [Greentea](https://github.com/ARMmbed/mbed-os-tools/tree/master/packages/mbed-greentea) and [Htrun](https://github.com/ARMmbed/mbed-os-tools/tree/master/packages/mbed-host-tests), It enables enables Fast Models to run Mbed OS Greentea test suites. If user only need to run mbed OS applications or examples rather than Greentea tests on Fast Models, please referencing [this mbed OS documents](https://os.mbed.com/docs/v5.10/tools/fast-models.html) ## Requirements -1. Make sure you have Arm Fast Models Libraries files installed to your host machines, as well as the Fast Models PyCADI. -2. A valid Fast Models license been set up correctly. -2. Install [Greentea](https://github.com/ARMmbed/greentea) latest master from GitHub (current 1.4.0 release do not contains the support for Fast Models) -3. Htrun Version 1.4.1 or later + 1. Make sure you have Arm Fast Models Libraries files installed to your host machines, as well as the Fast Models PyCADI. + +>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 +``` +pip install mbed-greentea -U +mbedgt --version +``` + 4. Htrun version 1.4.1 or later +``` +pip install mbed-host-tests -U +mbedhtrun --version +``` ## Download ``` @@ -114,4 +128,5 @@ Then users need to edit `mbed-fastmodel-agent\fm_agent\settings.json` file eithe Key `configs_add` can be added for additional config files for each model, Or Key `config` can be added to overwrite `GLOBAL` config files. ## Known limitations: -1. Fast Models normally have 3 or 4 serial terminal ports. But currently only one port is supported at moment. +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 new file mode 100644 index 0000000..6ea17f1 --- /dev/null +++ b/fm_agent/configs/COVERAGE.conf @@ -0,0 +1,21 @@ +## 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 + +## 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/DEFAULT.conf b/fm_agent/configs/DEFAULT.conf index 9b69155..613023e 100644 --- a/fm_agent/configs/DEFAULT.conf +++ b/fm_agent/configs/DEFAULT.conf @@ -4,7 +4,7 @@ fvp_mps2.telnetterminal2.quiet=1 ## Control the output format of the telnet terminal -fvp_mps2.telnetterminal1.mode=raw +fvp_mps2.telnetterminal0.mode=raw ## Suppress the telnet/xterm to be launched, so that model agent can talk to the port @@ -12,4 +12,4 @@ fvp_mps2.telnetterminal0.start_telnet=0 ## logging the UART output to a file -# fvp_mps2.UART1.out_file=out_file.txt +# fvp_mps2.UART0.out_file=out_file.txt diff --git a/fm_agent/configs/FAST.conf b/fm_agent/configs/FAST.conf index c067f63..874691a 100644 --- a/fm_agent/configs/FAST.conf +++ b/fm_agent/configs/FAST.conf @@ -4,7 +4,7 @@ fvp_mps2.telnetterminal2.quiet=1 ## Control the output format of the telnet terminal -fvp_mps2.telnetterminal1.mode=raw +fvp_mps2.telnetterminal0.mode=raw ## Suppress the telnet/xterm to be launched, so that model agent can talk to the port @@ -12,7 +12,7 @@ fvp_mps2.telnetterminal0.start_telnet=0 ## logging the UART output to a file -# fvp_mps2.UART1.out_file=out_file.txt +# fvp_mps2.UART0.out_file=out_file.txt ## turn the rate limite off -fvp_mps2.mps2_visualisation.rate_limit-enable=0 \ No newline at end of file +fvp_mps2.mps2_visualisation.rate_limit-enable=0 diff --git a/fm_agent/configs/NETWORK.conf b/fm_agent/configs/NETWORK.conf index 4a15992..0c6bf03 100644 --- a/fm_agent/configs/NETWORK.conf +++ b/fm_agent/configs/NETWORK.conf @@ -4,7 +4,7 @@ fvp_mps2.telnetterminal2.quiet=1 ## Control the output format of the telnet terminal -fvp_mps2.telnetterminal1.mode=raw +fvp_mps2.telnetterminal0.mode=raw ## Suppress the telnet/xterm to be launched, so that model agent can talk to the port @@ -12,8 +12,8 @@ fvp_mps2.telnetterminal0.start_telnet=0 ## Logging the UART output to a file -# fvp_mps2.UART1.out_file=out_file.txt +# fvp_mps2.UART0.out_file=out_file.txt ## Enable Ethernet Port fvp_mps2.hostbridge.userNetworking=1 -fvp_mps2.smsc_91c111.enabled=1 \ No newline at end of file +fvp_mps2.smsc_91c111.enabled=1 diff --git a/fm_agent/fm_agent.py b/fm_agent/fm_agent.py index 076f3d6..6bc48b3 100644 --- a/fm_agent/fm_agent.py +++ b/fm_agent/fm_agent.py @@ -83,7 +83,8 @@ def setup_simulator(self, model_name, model_config): def __connect_terminal(self): """ connect socket terminal to a launched fastmodel""" - + self.logger.prn_inf("Establishing socket connection to FastModel Terminal") + time.sleep(2) try: self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.connect((self.host, self.port)) @@ -146,13 +147,22 @@ def run_simulator(self): def reset_simulator(self): """ reset a launched fastmodel and connect terminal """ if self.is_simulator_alive(): - self.model.stop() + self.logger.prn_wrn("STOP and RESTART FastModel") + self.__closeConnection() + self.model.release(shutdown=True) + time.sleep(1) + import fm.debug + self.model = fm.debug.LibraryModel(self.model_lib, self.model_params) + # check which host socket port is used for terminal0 + terminal = self.model.get_target('fvp_mps2.telnetterminal0') + self.port = terminal.read_register('Port') cpu = self.model.get_cpus()[0] - cpu.reset() if self.image: cpu.load_application(self.image) + self.logger.prn_wrn("RELOAD new image to FastModel") self.model.run(blocking=False) self.__connect_terminal() + self.logger.prn_wrn("Reconnect Terminal") return True else: return False @@ -219,9 +229,90 @@ def __closeConnection(self): else: self.logger.prn_inf("Terminal socket connection already closed") + def __run_to_breakpoint(self): + try: + self.model.run(timeout=15) + except: + # On timeout, model hangs + self.logger.prn_err("ERROR: Timeout reached without stop at breakpoint") + self.model.stop() + return False + else: + return True + + def __CodeCoverage(self): + """ runs code coverage dump gcda file """ + + self.model.stop() + cpu = self.model.get_cpus()[0] + + symbol_table = [] + self.logger.prn_inf("Reading symbols from %s" % self.image) + if self.image: + symbol_table = read_symbol(self.image) + + data_hex_addr = get_symbol_addr(symbol_table, "__gcov_var__ported") + data_int_addr = HexToInt(data_hex_addr) + self.logger.prn_inf("Address for [__gcov_var__ported] is %s" % data_hex_addr ) + + dump_hex_addr = get_symbol_addr(symbol_table, "__gcov_close__ported") + dump_int_addr = HexToInt(dump_hex_addr) + self.logger.prn_inf("Address for [__gcov_close__ported] is %s" % dump_hex_addr ) + + exit_hex_addr = get_symbol_addr(symbol_table, "collect_coverage") + exit_int_addr = HexToInt(exit_hex_addr) + self.logger.prn_inf("Address for [collect_coverage] is %s" % exit_hex_addr ) + + + + self.logger.prn_inf("Setting breakpoints...") + bkpt_dump = cpu.add_bpt_prog( dump_int_addr + 57 ) + bkpt_exit = cpu.add_bpt_prog( exit_int_addr + 43 ) + + self.logger.prn_inf("Removing old gcda files...") + remove_gcda() + + self.__run_to_breakpoint() + stopped_loc = cpu.read_register('Core.R15') + + while stopped_loc == bkpt_dump.address : + + start_addr = ByteToInt( cpu.read_memory( data_int_addr , size=4 ) ) + end_addr = ByteToInt( cpu.read_memory( data_int_addr+4 , size=4 ) ) + file_var_addr = ByteToInt( cpu.read_memory( data_int_addr+8 , size=4 ) ) + + file_var = r'' + mem_char = " " + + while ord(mem_char) != 0: + mem_char = cpu.read_memory(file_var_addr) + file_var += str(mem_char) + file_var_addr += 1 + + filename = file_var.rstrip(' \t\r\n\0') + self.logger.prn_inf("dumping to " + filename) + with open(filename, "wb") as f: + mem = cpu.read_memory(start_addr, count=(end_addr-start_addr)) + f.write(mem) + + if self.__run_to_breakpoint(): + stopped_loc = cpu.read_register('Core.R15') + else: + stopped_loc = cpu.read_register('Core.R15') + break + + + if stopped_loc == bkpt_exit.address: + self.logger.prn_inf("Coverage dump program run to the end.") + else: + self.logger.prn_wrn("Coverage dump ended somewhere else!!") + lcov_collect(os.path.basename(self.image)) + def shutdown_simulator(self): """ shutdown fastmodel if any """ if self.is_simulator_alive(): + if self.config_name == "COVERAGE": + self.__CodeCoverage() self.logger.prn_inf("Fast-Model agent shutting down model") self.__closeConnection() self.model.release(shutdown=True) diff --git a/fm_agent/mbedfm.py b/fm_agent/mbedfm.py index 7cb9041..4544f33 100644 --- a/fm_agent/mbedfm.py +++ b/fm_agent/mbedfm.py @@ -34,12 +34,12 @@ def print_version(): print(get_version()) def print_models(): - print list_fastmodels() - print "Import PyCADI Test ... {}".format("PASSED" if check_import() else "FAILED") + print(list_fastmodels()) + print("Import PyCADI 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(list_fastmodels(check_models=True)) + print("Import PyCADI Test ... {}".format("PASSED" if check_import() else "FAILED")) def list_fastmodels(check_models=False): """! List all models and configs in fm_agent""" @@ -84,7 +84,7 @@ def list_fastmodels(check_models=False): c_test_cell.append("PASSED" if resource.is_simulator_alive() else "FAILED") resource.shutdown_simulator() except Exception as e: - print str(e) + print(str(e)) c_test_cell.append("FAILED") else: c_avail_cell.append("NO 'CONFIG FILE' NOT EXIST") diff --git a/fm_agent/settings.json b/fm_agent/settings.json index fc99c9d..4f929bc 100644 --- a/fm_agent/settings.json +++ b/fm_agent/settings.json @@ -1,21 +1,22 @@ { "GLOBAL": { "Windows":{ - "PyCADI_path": "C:\\Program Files\\ARM\\FastModelsPortfolio_11.4\\lib\\python27", + "PyCADI_path": "C:\\work\\model_libs\\python27", "model_lib_path": "C:\\work\\model_libs", "configs": { "DEFAULT": "DEFAULT.conf", "NETWORK": "NETWORK.conf", - "FAST": "FAST.conf" + "COVERAGE":"COVERAGE.conf" } }, "Linux":{ - "PyCADI_path": "/home/user/ARM/FastModelsPortfolio_11.4/lib/python2.7/", - "model_lib_path": "/home/user/model_libs", + "PyCADI_path": "/work/model_libs/python2.7/", + "model_lib_path": "/work/model_libs", "configs": { "DEFAULT": "DEFAULT.conf", + "FAST": "FAST.conf", "NETWORK": "NETWORK.conf", - "FAST": "FAST.conf" + "COVERAGE":"COVERAGE.conf" } } }, diff --git a/fm_agent/utils.py b/fm_agent/utils.py index dc964bb..45308dc 100644 --- a/fm_agent/utils.py +++ b/fm_agent/utils.py @@ -23,6 +23,7 @@ import time import socket import logging +import subprocess from functools import partial class SimulatorError(Exception): @@ -94,8 +95,8 @@ def check_import(): import fm.debug except ImportError as e: for warning in warning_msgs: - print warning - print "Error: Failed to import fast models PyCADI!!!" + print(warning) + print("Error: Failed to import fast models PyCADI!!!") return False else: return True @@ -172,3 +173,44 @@ def find_free_port(): s.close() return port + """ the following function mainly for coverage mode """ + +def read_symbol(image): + """this function reads images symbol to a global variable""" + symbol_table = [] + try: + symbol_table = subprocess.check_output('arm-none-eabi-readelf -sW "{}"'.format(image), shell=True).split("\n") + except Exception as e: + print("Make sure you have arm-none-eabi-readelf tool in PATH") + print("ERROR - {}.".format(str(e))) + sys.exit(1) + return symbol_table + +def get_symbol_addr(symbol_table, symbol_name): + """ + Num: Value Size Type Bind Vis Ndx Name + 24: 0002f45a 0 NOTYPE LOCAL DEFAULT 2 init_bss + 25: 0002f470 0 NOTYPE LOCAL DEFAULT 2 system_startup + 26: 0002f468 0 NOTYPE LOCAL DEFAULT 2 zero + """ + for line in symbol_table: + data = line.split() + if symbol_name in data: + return data[1] + +def ByteToInt( byteList ): + return int(''.join( [ "{:02x}".format(x) for x in reversed(byteList) ] ),16) + +def HexToInt( hex ): + return int(hex,16) + +def lcov_collect(filename): + """this function reads images symbol to a global variable""" + subprocess.call('lcov -c -d . --no-external -o BUILD/{}.info'.format(filename), shell=True) + +def remove_gcda(rootdir="."): + """this function removes gcda files""" + for root, dirs, files in os.walk(rootdir): + for file in files: + if file.endswith(".gcda"): + os.remove(os.path.join(root, file)) diff --git a/setup.py b/setup.py index f177b6a..9a1e47c 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ """ mbed SDK -Copyright (c) 2011-2018 ARM Limited +Copyright (c) 2018-2019 ARM Limited Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -51,5 +51,10 @@ def read(fname): license=LICENSE, test_suite = 'test', include_package_data=True, - install_requires=["PrettyTable>=0.7.2"]) + install_requires=[ + "PrettyTable>=0.7.2", + "mbed-host-tests>=1.4.1", + "mbed-greentea>=1.5.0" + ] +)