From d84c5074f5606ae6c1753b700382cb4b042ecaf1 Mon Sep 17 00:00:00 2001 From: Chris Reed Date: Sat, 6 May 2017 15:22:40 -0500 Subject: [PATCH 1/2] Fixed halt on connect and disconnect resume. - Control over halt on connect and disconnect resume was broken because the option flags were not being passed to the CortexM class. --- pyOCD/board/board.py | 11 ++--------- pyOCD/core/coresight_target.py | 5 +++-- pyOCD/core/target.py | 2 +- pyOCD/coresight/cortex_m.py | 6 ++++-- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/pyOCD/board/board.py b/pyOCD/board/board.py index 9204f39b1..eab3b9aa5 100644 --- a/pyOCD/board/board.py +++ b/pyOCD/board/board.py @@ -53,22 +53,15 @@ def init(self): def uninit(self, resume=True): """ Uninitialize the board: link and target. - This function resumes the target """ if self.closed: return self.closed = True - logging.debug("uninit board %s", self) - if resume and self.initiated: - try: - self.target.resume() - except: - logging.error("target exception during uninit:") - traceback.print_exc() + logging.debug("uninit board %s, resuming=%s", self, resume) if self.initiated: try: - self.target.disconnect() + self.target.disconnect(resume) self.initiated = False except: logging.error("link exception during target disconnect:") diff --git a/pyOCD/core/coresight_target.py b/pyOCD/core/coresight_target.py index a0168afa1..a49a83aff 100644 --- a/pyOCD/core/coresight_target.py +++ b/pyOCD/core/coresight_target.py @@ -94,16 +94,17 @@ def init(self, bus_accessible=True): # Create CortexM core. core0 = cortex_m.CortexM(self, self.dp, self.aps[0], self.memory_map) + core0.setHaltOnConnect(self.halt_on_connect) if bus_accessible: core0.init() self.add_core(core0) self.notify(Notification(event=Target.EVENT_POST_CONNECT, source=self)) - def disconnect(self): + def disconnect(self, resume=True): self.notify(Notification(event=Target.EVENT_PRE_DISCONNECT, source=self)) for core in self.cores.values(): - core.disconnect() + core.disconnect(resume) self.dp.power_down_debug() def readIDCode(self): diff --git a/pyOCD/core/target.py b/pyOCD/core/target.py index f3b85d012..6e513a103 100644 --- a/pyOCD/core/target.py +++ b/pyOCD/core/target.py @@ -102,7 +102,7 @@ def setFlash(self, flash): def init(self): raise NotImplementedError() - def disconnect(self): + def disconnect(self, resume=True): pass def info(self, request): diff --git a/pyOCD/coresight/cortex_m.py b/pyOCD/coresight/cortex_m.py index 293b33933..395aedd87 100644 --- a/pyOCD/coresight/cortex_m.py +++ b/pyOCD/coresight/cortex_m.py @@ -319,7 +319,7 @@ def init(self): self.dwt.init() self.sw_bp.init() - def disconnect(self): + def disconnect(self, resume=True): # Remove breakpoints. self.bp_manager.remove_all_breakpoints() @@ -327,7 +327,9 @@ def disconnect(self): self.write32(CortexM.DEMCR, 0) # Disable core debug. - self.write32(CortexM.DHCSR, CortexM.DBGKEY | 0x0000) + if resume: + self.resume() + self.write32(CortexM.DHCSR, CortexM.DBGKEY | 0x0000) def buildTargetXML(self): # Build register_list and targetXML From 477f82c7e41cfb9b3e26595e95f10df50c535ecb Mon Sep 17 00:00:00 2001 From: Chris Reed Date: Sat, 6 May 2017 16:41:31 -0500 Subject: [PATCH 2/2] New test to validate halt on connect and disconnect resume functionality. --- test/automated_test.py | 3 +- test/connect_test.py | 179 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 test/connect_test.py diff --git a/test/automated_test.py b/test/automated_test.py index 6396dae55..69c899ab8 100644 --- a/test/automated_test.py +++ b/test/automated_test.py @@ -35,7 +35,7 @@ from flash_test import FlashTest from gdb_test import GdbTest from gdb_server_json_test import GdbServerJsonTest - +from connect_test import ConnectTest def print_summary(test_list, result_list, test_time, output_file=None): for test in test_list: @@ -76,6 +76,7 @@ def main(): test = Test("Basic Test", lambda board: basic_test(board, None)) test_list.append(test) test_list.append(GdbServerJsonTest()) + test_list.append(ConnectTest()) test_list.append(SpeedTest()) test_list.append(CortexTest()) test_list.append(FlashTest()) diff --git a/test/connect_test.py b/test/connect_test.py new file mode 100644 index 000000000..4df7c0947 --- /dev/null +++ b/test/connect_test.py @@ -0,0 +1,179 @@ +""" + mbed CMSIS-DAP debugger + Copyright (c) 2017-2018 ARM Limited + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +from __future__ import print_function + +import os, sys +import traceback +import argparse +from collections import namedtuple + +parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +sys.path.insert(0, parentdir) + +import pyOCD +from pyOCD.board import MbedBoard +from pyOCD.core.target import Target +from test_util import Test, TestResult +import logging + +STATE_NAMES = { + Target.TARGET_RUNNING : "running", + Target.TARGET_HALTED : "halted", + Target.TARGET_RESET : "reset", + Target.TARGET_SLEEPING : "sleeping", + Target.TARGET_LOCKUP : "lockup", + } + +RUNNING = Target.TARGET_RUNNING +HALTED = Target.TARGET_HALTED + +class ConnectTestCase(object): + def __init__(self, prev_exit_state, halt_on_connect, expected_state, disconnect_resume, exit_state): + self.prev_exit_state = prev_exit_state + self.halt_on_connect = halt_on_connect + self.expected_state = expected_state + self.disconnect_resume = disconnect_resume + self.exit_state = exit_state + +class ConnectTestResult(TestResult): + def __init__(self): + super(ConnectTestResult, self).__init__(None, None, None) + +class ConnectTest(Test): + def __init__(self): + super(ConnectTest, self).__init__("Connect Test", connect_test) + + def run(self, board): + try: + result = self.test_function(board) + except Exception as e: + print("Exception %s when testing board %s" % (e, board.getUniqueID())) + result = ConnectTestResult() + result.passed = False + traceback.print_exc(file=sys.stdout) + result.board = board + result.test = self + return result + + +def connect_test(board): + board_id = board.getUniqueID() + binary_file = os.path.join(parentdir, 'binaries', board.getTestBinary()) + print("binary file: %s" % binary_file) + + test_pass_count = 0 + test_count = 0 + result = ConnectTestResult() + + # Install binary. + live_board = MbedBoard.chooseBoard(board_id=board_id, frequency=1000000) + memory_map = board.target.getMemoryMap() + rom_region = memory_map.getBootMemory() + rom_start = rom_region.start + + def test_connect(halt_on_connect, expected_state, resume): + print("Connecting with halt_on_connect=%s" % halt_on_connect) + live_board = MbedBoard.chooseBoard(board_id=board_id, frequency=1000000, init_board=False) + live_board.target.setHaltOnConnect(halt_on_connect) + live_board.init() + print("Verifying target is", STATE_NAMES.get(expected_state, "unknown")) + actualState = live_board.target.getState() + if actualState == expected_state: + passed = 1 + print("TEST PASSED") + else: + passed = 0 + print("TEST FAILED (state={}, expected={})".format( + STATE_NAMES.get(actualState, "unknown"), + STATE_NAMES.get(expected_state, "unknown"))) + print("Disconnecting with resume=%s" % resume) + live_board.uninit(resume) + live_board = None + return passed + + # TEST CASE COMBINATIONS + test_cases = [ + # + ConnectTestCase( RUNNING, False, RUNNING, False, RUNNING ), + ConnectTestCase( RUNNING, True, HALTED, False, HALTED ), + ConnectTestCase( HALTED, True, HALTED, True, RUNNING ), + ConnectTestCase( RUNNING, True, HALTED, True, RUNNING ), + ConnectTestCase( RUNNING, False, RUNNING, True, RUNNING ), + ConnectTestCase( RUNNING, True, HALTED, False, HALTED ), + ConnectTestCase( HALTED, False, HALTED, False, HALTED ), + ConnectTestCase( HALTED, True, HALTED, False, HALTED ), + ConnectTestCase( HALTED, False, HALTED, True, RUNNING ), + ConnectTestCase( RUNNING, False, RUNNING, False, RUNNING ), + ] + + print("\n\n----- FLASH NEW BINARY -----") + live_board.flash.flashBinary(binary_file, rom_start) + live_board.target.reset() + test_count += 1 + print("Verifying target is running") + if live_board.target.isRunning(): + test_pass_count += 1 + print("TEST PASSED") + else: + print("TEST FAILED") + print("Disconnecting with resume=True") + live_board.uninit(resume=True) + live_board = None + # Leave running. + + # Run all the cases. + for case in test_cases: + test_count += 1 + did_pass = test_connect( + halt_on_connect=case.halt_on_connect, + expected_state=case.expected_state, + resume=case.disconnect_resume + ) + test_pass_count += did_pass + case.passed=did_pass + + print("\n\nTest Summary:") + print("\n{:<4}{:<12}{:<19}{:<12}{:<21}{:<11}{:<10}".format( + "#", "Prev Exit", "Halt on Connect", "Expected", "Disconnect Resume", "Exit", "Passed")) + for i, case in enumerate(test_cases): + print("{:<4}{:<12}{:<19}{:<12}{:<21}{:<11}{:<10}".format( + i, + STATE_NAMES[case.prev_exit_state], + repr(case.halt_on_connect), + STATE_NAMES[case.expected_state], + repr(case.disconnect_resume), + STATE_NAMES[case.exit_state], + "PASS" if case.passed else "FAIL")) + print("\nPass count %i of %i tests" % (test_pass_count, test_count)) + if test_pass_count == test_count: + print("CONNECT TEST SCRIPT PASSED") + else: + print("CONNECT TEST SCRIPT FAILED") + + result.passed = (test_count == test_pass_count) + return result + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='pyOCD connect test') + parser.add_argument('-d', '--debug', action="store_true", help='Enable debug logging') + args = parser.parse_args() + level = logging.DEBUG if args.debug else logging.INFO + logging.basicConfig(level=level) + board = pyOCD.board.mbed_board.MbedBoard.getAllConnectedBoards(close=True)[0] + test = ConnectTest() + result = [test.run(board)] + test.print_perf_info(result)