mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #11023 from ARMmbed/release-candidate
Release candidate for mbed-os-5.13.1pull/11094/head mbed-os-5.13.1
commit
5941d17183
|
@ -127,7 +127,17 @@ class PyusbMSDTest(BaseHostTest):
|
||||||
else:
|
else:
|
||||||
self.report_error("unmount")
|
self.report_error("unmount")
|
||||||
|
|
||||||
|
def _callback_os_type(self, key, value, timestamp):
|
||||||
|
system_name = platform.system()
|
||||||
|
if system_name == "Windows":
|
||||||
|
self.send_kv("os_type", 1)
|
||||||
|
elif system_name == "Linux":
|
||||||
|
self.send_kv("os_type", 2)
|
||||||
|
elif system_name == "Darwin":
|
||||||
|
self.send_kv("os_type", 3)
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
|
self.register_callback("get_os_type", self._callback_os_type)
|
||||||
self.register_callback("get_serial_number", self._callback_device_ready)
|
self.register_callback("get_serial_number", self._callback_device_ready)
|
||||||
self.register_callback('check_if_mounted', self._callback_check_if_mounted)
|
self.register_callback('check_if_mounted', self._callback_check_if_mounted)
|
||||||
self.register_callback('check_if_not_mounted', self._callback_check_if_not_mounted)
|
self.register_callback('check_if_not_mounted', self._callback_check_if_not_mounted)
|
||||||
|
@ -204,25 +214,16 @@ class MSDUtils(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _unmount_windows(serial):
|
def _unmount_windows(serial):
|
||||||
disk_path = MSDUtils._disk_path_windows(serial)
|
disk_path = MSDUtils._disk_path_windows(serial)
|
||||||
tmp_file = tempfile.NamedTemporaryFile(suffix='.ps1', delete=False)
|
cmd_string = r'(New-Object -comObject Shell.Application).Namespace(17).ParseName("{}").InvokeVerb("Eject")'.format(disk_path)
|
||||||
try:
|
|
||||||
# create unmount script
|
|
||||||
tmp_file.write('$disk_leter=$args[0]\n')
|
|
||||||
tmp_file.write('$driveEject = New-Object -comObject Shell.Application\n')
|
|
||||||
tmp_file.write('$driveEject.Namespace(17).ParseName($disk_leter).InvokeVerb("Eject")\n')
|
|
||||||
# close to allow open by other process
|
|
||||||
tmp_file.close()
|
|
||||||
|
|
||||||
try_count = 10
|
try_count = 10
|
||||||
while try_count:
|
while try_count:
|
||||||
p = subprocess.Popen(["powershell.exe", tmp_file.name + " " + disk_path], stdout=sys.stdout)
|
p = subprocess.Popen(["powershell.exe", cmd_string], stdout=sys.stdout)
|
||||||
p.communicate()
|
p.communicate()
|
||||||
try_count -= 1
|
try_count -= 1
|
||||||
if MSDUtils._disk_path_windows(serial) is None:
|
if MSDUtils._disk_path_windows(serial) is None:
|
||||||
return True
|
return True
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
finally:
|
|
||||||
os.remove(tmp_file.name)
|
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
"""
|
||||||
|
Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
from mbed_host_tests import BaseHostTest
|
||||||
|
from mbed_host_tests.host_tests_runner.host_test_default import DefaultTestSelector
|
||||||
|
|
||||||
|
DEFAULT_SYNC_DELAY = 4.0
|
||||||
|
|
||||||
|
MSG_VALUE_WATCHDOG_PRESENT = 'wdg_present'
|
||||||
|
MSG_VALUE_DUMMY = '0'
|
||||||
|
MSG_VALUE_RESET_REASON_GET = 'get'
|
||||||
|
MSG_VALUE_RESET_REASON_CLEAR = 'clear'
|
||||||
|
MSG_VALUE_DEVICE_RESET_NVIC = 'nvic'
|
||||||
|
MSG_VALUE_DEVICE_RESET_WATCHDOG = 'watchdog'
|
||||||
|
|
||||||
|
MSG_KEY_DEVICE_READY = 'ready'
|
||||||
|
MSG_KEY_RESET_REASON_RAW = 'reason_raw'
|
||||||
|
MSG_KEY_RESET_REASON = 'reason'
|
||||||
|
MSG_KEY_DEVICE_RESET = 'reset'
|
||||||
|
MSG_KEY_SYNC = '__sync'
|
||||||
|
|
||||||
|
RESET_REASONS = {
|
||||||
|
'POWER_ON': '0',
|
||||||
|
'PIN_RESET': '1',
|
||||||
|
'BROWN_OUT': '2',
|
||||||
|
'SOFTWARE': '3',
|
||||||
|
'WATCHDOG': '4',
|
||||||
|
'LOCKUP': '5',
|
||||||
|
'WAKE_LOW_POWER': '6',
|
||||||
|
'ACCESS_ERROR': '7',
|
||||||
|
'BOOT_ERROR': '8',
|
||||||
|
'MULTIPLE': '9',
|
||||||
|
'PLATFORM': '10',
|
||||||
|
'UNKNOWN': '11'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def raise_if_different(expected, actual, text=''):
|
||||||
|
"""Raise a RuntimeError if actual is different than expected."""
|
||||||
|
if expected != actual:
|
||||||
|
raise RuntimeError('{}Got {!r}, expected {!r}'
|
||||||
|
.format(text, actual, expected))
|
||||||
|
|
||||||
|
|
||||||
|
class ResetReasonTest(BaseHostTest):
|
||||||
|
"""Test for the Reset Reason HAL API.
|
||||||
|
|
||||||
|
Given a device supporting a Reset Reason API.
|
||||||
|
When the device is restarted using various methods.
|
||||||
|
Then the device returns a correct reset reason for every restart.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(ResetReasonTest, self).__init__()
|
||||||
|
self.device_has_watchdog = None
|
||||||
|
self.raw_reset_reasons = set()
|
||||||
|
self.sync_delay = DEFAULT_SYNC_DELAY
|
||||||
|
self.test_steps_sequence = self.test_steps()
|
||||||
|
# Advance the coroutine to it's first yield statement.
|
||||||
|
self.test_steps_sequence.send(None)
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
sync_delay = self.get_config_item('forced_reset_timeout')
|
||||||
|
self.sync_delay = sync_delay if sync_delay is not None else DEFAULT_SYNC_DELAY
|
||||||
|
self.register_callback(MSG_KEY_DEVICE_READY, self.cb_device_ready)
|
||||||
|
self.register_callback(MSG_KEY_RESET_REASON_RAW, self.cb_reset_reason_raw)
|
||||||
|
self.register_callback(MSG_KEY_RESET_REASON, self.cb_reset_reason)
|
||||||
|
self.register_callback(MSG_KEY_DEVICE_RESET, self.cb_reset_reason)
|
||||||
|
|
||||||
|
def cb_device_ready(self, key, value, timestamp):
|
||||||
|
"""Request a raw value of the reset_reason register.
|
||||||
|
|
||||||
|
Additionally, save the device's watchdog status on the first call.
|
||||||
|
"""
|
||||||
|
if self.device_has_watchdog is None:
|
||||||
|
self.device_has_watchdog = (value == MSG_VALUE_WATCHDOG_PRESENT)
|
||||||
|
self.send_kv(MSG_KEY_RESET_REASON_RAW, MSG_VALUE_RESET_REASON_GET)
|
||||||
|
|
||||||
|
def cb_reset_reason_raw(self, key, value, timestamp):
|
||||||
|
"""Verify that the raw reset_reason register value is unique.
|
||||||
|
|
||||||
|
Fail the test suite if the raw reset_reason value is not unique.
|
||||||
|
Request a platform independent reset_reason otherwise.
|
||||||
|
"""
|
||||||
|
if value in self.raw_reset_reasons:
|
||||||
|
self.log('TEST FAILED: The raw reset reason is not unique. '
|
||||||
|
'{!r} is already present in {!r}.'
|
||||||
|
.format(value, self.raw_reset_reasons))
|
||||||
|
self.notify_complete(False)
|
||||||
|
else:
|
||||||
|
self.raw_reset_reasons.add(value)
|
||||||
|
self.send_kv(MSG_KEY_RESET_REASON, MSG_VALUE_RESET_REASON_GET)
|
||||||
|
|
||||||
|
def cb_reset_reason(self, key, value, timestamp):
|
||||||
|
"""Feed the test_steps coroutine with reset_reason value.
|
||||||
|
|
||||||
|
Pass the test suite if the coroutine yields True.
|
||||||
|
Fail the test suite if the iterator stops or raises a RuntimeError.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if self.test_steps_sequence.send(value):
|
||||||
|
self.notify_complete(True)
|
||||||
|
except (StopIteration, RuntimeError) as exc:
|
||||||
|
self.log('TEST FAILED: {}'.format(exc))
|
||||||
|
self.notify_complete(False)
|
||||||
|
|
||||||
|
def test_steps(self):
|
||||||
|
"""Generate a sequence of test steps.
|
||||||
|
|
||||||
|
This coroutine calls yield to wait for the input from the device
|
||||||
|
(the reset_reason). If the device gives the wrong response, the
|
||||||
|
generator raises a RuntimeError exception and fails the test.
|
||||||
|
"""
|
||||||
|
# Ignore the first reason.
|
||||||
|
__ignored_reset_reason = yield
|
||||||
|
self.raw_reset_reasons.clear()
|
||||||
|
self.send_kv(MSG_KEY_RESET_REASON, MSG_VALUE_RESET_REASON_CLEAR)
|
||||||
|
__ignored_clear_ack = yield
|
||||||
|
|
||||||
|
# Request a NVIC_SystemReset() call.
|
||||||
|
self.send_kv(MSG_KEY_DEVICE_RESET, MSG_VALUE_DEVICE_RESET_NVIC)
|
||||||
|
__ignored_reset_ack = yield
|
||||||
|
time.sleep(self.sync_delay)
|
||||||
|
self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY)
|
||||||
|
reset_reason = yield
|
||||||
|
raise_if_different(RESET_REASONS['SOFTWARE'], reset_reason, 'Wrong reset reason. ')
|
||||||
|
self.send_kv(MSG_KEY_RESET_REASON, MSG_VALUE_RESET_REASON_CLEAR)
|
||||||
|
__ignored_clear_ack = yield
|
||||||
|
|
||||||
|
# Reset the device using DAP.
|
||||||
|
self.reset_dut(DefaultTestSelector.RESET_TYPE_SW_RST)
|
||||||
|
reset_reason = yield
|
||||||
|
raise_if_different(RESET_REASONS['PIN_RESET'], reset_reason, 'Wrong reset reason. ')
|
||||||
|
self.send_kv(MSG_KEY_RESET_REASON, MSG_VALUE_RESET_REASON_CLEAR)
|
||||||
|
__ignored_clear_ack = yield
|
||||||
|
|
||||||
|
# Start a watchdog timer and wait for it to reset the device.
|
||||||
|
if not self.device_has_watchdog:
|
||||||
|
self.log('DUT does not have a watchdog. Skipping this reset reason.')
|
||||||
|
else:
|
||||||
|
self.send_kv(MSG_KEY_DEVICE_RESET, MSG_VALUE_DEVICE_RESET_WATCHDOG)
|
||||||
|
__ignored_reset_ack = yield
|
||||||
|
time.sleep(self.sync_delay)
|
||||||
|
self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY)
|
||||||
|
reset_reason = yield
|
||||||
|
raise_if_different(RESET_REASONS['WATCHDOG'], reset_reason, 'Wrong reset reason. ')
|
||||||
|
|
||||||
|
# The sequence is correct -- test passed.
|
||||||
|
yield True
|
|
@ -0,0 +1,74 @@
|
||||||
|
"""
|
||||||
|
Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
from mbed_host_tests import BaseHostTest
|
||||||
|
|
||||||
|
DEFAULT_SYNC_DELAY = 4.0
|
||||||
|
|
||||||
|
MSG_VALUE_DUMMY = '0'
|
||||||
|
MSG_KEY_DEVICE_READY = 'ready'
|
||||||
|
MSG_KEY_START_CASE = 'start_case'
|
||||||
|
MSG_KEY_DEVICE_RESET = 'reset_on_case_teardown'
|
||||||
|
MSG_KEY_SYNC = '__sync'
|
||||||
|
|
||||||
|
|
||||||
|
class SyncOnReset(BaseHostTest):
|
||||||
|
"""Host side test that handles device reset during case teardown.
|
||||||
|
|
||||||
|
Given a device that performs a reset during a test case teardown.
|
||||||
|
When the device notifies the host about the reset.
|
||||||
|
Then the host:
|
||||||
|
* keeps track of the test case index of the current test suite,
|
||||||
|
* performs a dev-host handshake,
|
||||||
|
* advances the test suite to next test case.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
Developed for a watchdog test, so that it can be run on devices that
|
||||||
|
do not support watchdog timeout updates after the initial setup.
|
||||||
|
As a solution, after testing watchdog with one set of settings, the
|
||||||
|
device performs a reset and notifies the host with the test case number,
|
||||||
|
so that the test suite may be advanced once the device boots again.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(SyncOnReset, self).__init__()
|
||||||
|
self.test_case_num = 0
|
||||||
|
self.sync_delay = DEFAULT_SYNC_DELAY
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
sync_delay = self.get_config_item('forced_reset_timeout')
|
||||||
|
self.sync_delay = sync_delay if sync_delay is not None else DEFAULT_SYNC_DELAY
|
||||||
|
self.register_callback(MSG_KEY_DEVICE_READY, self.cb_device_ready)
|
||||||
|
self.register_callback(MSG_KEY_DEVICE_RESET, self.cb_device_reset)
|
||||||
|
|
||||||
|
def cb_device_ready(self, key, value, timestamp):
|
||||||
|
"""Advance the device test suite to the next test case."""
|
||||||
|
self.send_kv(MSG_KEY_START_CASE, self.test_case_num)
|
||||||
|
|
||||||
|
def cb_device_reset(self, key, value, timestamp):
|
||||||
|
"""Wait for the device to boot and perform a handshake.
|
||||||
|
|
||||||
|
Additionally, keep track of the last test case number.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self.test_case_num = int(value)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
self.test_case_num += 1
|
||||||
|
time.sleep(self.sync_delay)
|
||||||
|
self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY)
|
|
@ -0,0 +1,145 @@
|
||||||
|
"""
|
||||||
|
Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
import collections
|
||||||
|
import threading
|
||||||
|
from mbed_host_tests import BaseHostTest
|
||||||
|
|
||||||
|
TestCaseData = collections.namedtuple('TestCaseData', ['index', 'data_to_send'])
|
||||||
|
|
||||||
|
DEFAULT_SYNC_DELAY = 4.0
|
||||||
|
MAX_HB_PERIOD = 2.5 # [s] Max expected heartbeat period.
|
||||||
|
|
||||||
|
MSG_VALUE_DUMMY = '0'
|
||||||
|
CASE_DATA_INVALID = 0xffffffff
|
||||||
|
CASE_DATA_PHASE2_OK = 0xfffffffe
|
||||||
|
CASE_DATA_INSUFF_HB = 0x0
|
||||||
|
|
||||||
|
MSG_KEY_SYNC = '__sync'
|
||||||
|
MSG_KEY_DEVICE_READY = 'ready'
|
||||||
|
MSG_KEY_START_CASE = 'start_case'
|
||||||
|
MSG_KEY_DEVICE_RESET = 'dev_reset'
|
||||||
|
MSG_KEY_HEARTBEAT = 'hb'
|
||||||
|
|
||||||
|
|
||||||
|
class WatchdogReset(BaseHostTest):
|
||||||
|
"""Host side test that handles device reset.
|
||||||
|
|
||||||
|
Given a device with a watchdog timer started.
|
||||||
|
When the device notifies the host about an incoming reset.
|
||||||
|
Then the host:
|
||||||
|
* keeps track of the test case index of the current test suite,
|
||||||
|
* performs a dev-host handshake.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(WatchdogReset, self).__init__()
|
||||||
|
self.current_case = TestCaseData(0, CASE_DATA_INVALID)
|
||||||
|
self.__handshake_timer = None
|
||||||
|
self.sync_delay = DEFAULT_SYNC_DELAY
|
||||||
|
self.drop_heartbeat_messages = True
|
||||||
|
self.hb_timestamps_us = []
|
||||||
|
|
||||||
|
def handshake_timer_start(self, seconds=1.0, pre_sync_fun=None):
|
||||||
|
"""Start a new handshake timer."""
|
||||||
|
|
||||||
|
def timer_handler():
|
||||||
|
"""Perform a dev-host handshake by sending a sync message."""
|
||||||
|
if pre_sync_fun is not None:
|
||||||
|
pre_sync_fun()
|
||||||
|
self.send_kv(MSG_KEY_SYNC, MSG_VALUE_DUMMY)
|
||||||
|
|
||||||
|
self.__handshake_timer = threading.Timer(seconds, timer_handler)
|
||||||
|
self.__handshake_timer.start()
|
||||||
|
|
||||||
|
def handshake_timer_cancel(self):
|
||||||
|
"""Cancel the current handshake timer."""
|
||||||
|
try:
|
||||||
|
self.__handshake_timer.cancel()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
self.__handshake_timer = None
|
||||||
|
|
||||||
|
def heartbeat_timeout_handler(self):
|
||||||
|
"""Handler for the heartbeat timeout.
|
||||||
|
|
||||||
|
Compute the time span of the last heartbeat sequence.
|
||||||
|
Set self.current_case.data_to_send to CASE_DATA_INVALID if no heartbeat was received.
|
||||||
|
Set self.current_case.data_to_send to CASE_DATA_INSUFF_HB if only one heartbeat was
|
||||||
|
received.
|
||||||
|
"""
|
||||||
|
self.drop_heartbeat_messages = True
|
||||||
|
dev_data = CASE_DATA_INVALID
|
||||||
|
if len(self.hb_timestamps_us) == 1:
|
||||||
|
dev_data = CASE_DATA_INSUFF_HB
|
||||||
|
self.log('Not enough heartbeats received.')
|
||||||
|
elif len(self.hb_timestamps_us) >= 2:
|
||||||
|
dev_data = int(round(0.001 * (self.hb_timestamps_us[-1] - self.hb_timestamps_us[0])))
|
||||||
|
self.log('Heartbeat time span was {} ms.'.format(dev_data))
|
||||||
|
self.current_case = TestCaseData(self.current_case.index, dev_data)
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
sync_delay = self.get_config_item('forced_reset_timeout')
|
||||||
|
self.sync_delay = sync_delay if sync_delay is not None else DEFAULT_SYNC_DELAY
|
||||||
|
self.register_callback(MSG_KEY_DEVICE_READY, self.cb_device_ready)
|
||||||
|
self.register_callback(MSG_KEY_DEVICE_RESET, self.cb_device_reset)
|
||||||
|
self.register_callback(MSG_KEY_HEARTBEAT, self.cb_heartbeat)
|
||||||
|
|
||||||
|
def teardown(self):
|
||||||
|
self.handshake_timer_cancel()
|
||||||
|
|
||||||
|
def cb_device_ready(self, key, value, timestamp):
|
||||||
|
"""Advance the device test suite to a proper test case.
|
||||||
|
|
||||||
|
Additionally, send test case data to the device.
|
||||||
|
"""
|
||||||
|
self.handshake_timer_cancel()
|
||||||
|
msg_value = '{0.index:02x},{0.data_to_send:08x}'.format(self.current_case)
|
||||||
|
self.send_kv(MSG_KEY_START_CASE, msg_value)
|
||||||
|
self.drop_heartbeat_messages = False
|
||||||
|
self.hb_timestamps_us = []
|
||||||
|
|
||||||
|
def cb_device_reset(self, key, value, timestamp):
|
||||||
|
"""Keep track of the test case number.
|
||||||
|
|
||||||
|
Also set a new handshake timeout, so when the device gets
|
||||||
|
restarted by the watchdog, the communication will be restored
|
||||||
|
by the __handshake_timer.
|
||||||
|
"""
|
||||||
|
self.handshake_timer_cancel()
|
||||||
|
case_num, dev_reset_delay_ms = (int(i, base=16) for i in value.split(','))
|
||||||
|
self.current_case = TestCaseData(case_num, CASE_DATA_PHASE2_OK)
|
||||||
|
self.handshake_timer_start(self.sync_delay + dev_reset_delay_ms / 1000.0)
|
||||||
|
|
||||||
|
def cb_heartbeat(self, key, value, timestamp):
|
||||||
|
"""Save the timestamp of a heartbeat message.
|
||||||
|
|
||||||
|
Additionally, keep track of the test case number.
|
||||||
|
|
||||||
|
Also each heartbeat sets a new timeout, so when the device gets
|
||||||
|
restarted by the watchdog, the communication will be restored
|
||||||
|
by the __handshake_timer.
|
||||||
|
"""
|
||||||
|
if self.drop_heartbeat_messages:
|
||||||
|
return
|
||||||
|
self.handshake_timer_cancel()
|
||||||
|
case_num, timestamp_us = (int(i, base=16) for i in value.split(','))
|
||||||
|
self.current_case = TestCaseData(case_num, CASE_DATA_INVALID)
|
||||||
|
self.hb_timestamps_us.append(timestamp_us)
|
||||||
|
self.handshake_timer_start(
|
||||||
|
seconds=(MAX_HB_PERIOD + self.sync_delay),
|
||||||
|
pre_sync_fun=self.heartbeat_timeout_handler)
|
|
@ -144,7 +144,7 @@ void test_thread_safety()
|
||||||
TEST_ASSERT_EQUAL(0, ct.compute_partial_start(&crc));
|
TEST_ASSERT_EQUAL(0, ct.compute_partial_start(&crc));
|
||||||
TEST_ASSERT_EQUAL(0, ct.compute_partial((void *)&test, 4, &crc));
|
TEST_ASSERT_EQUAL(0, ct.compute_partial((void *)&test, 4, &crc));
|
||||||
|
|
||||||
Thread t1(osPriorityNormal1, 320);
|
Thread t1(osPriorityNormal1, 380);
|
||||||
t1.start(callback(test_thread));
|
t1.start(callback(test_thread));
|
||||||
TEST_ASSERT_EQUAL(0, ct.compute_partial((void *)&test[4], 5, &crc));
|
TEST_ASSERT_EQUAL(0, ct.compute_partial((void *)&test[4], 5, &crc));
|
||||||
TEST_ASSERT_EQUAL(0, ct.compute_partial_stop(&crc));
|
TEST_ASSERT_EQUAL(0, ct.compute_partial_stop(&crc));
|
||||||
|
|
|
@ -218,7 +218,7 @@ Case cases[] = {
|
||||||
|
|
||||||
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
||||||
{
|
{
|
||||||
GREENTEA_SETUP(test_timeout, "timing_drift_auto");
|
GREENTEA_SETUP(test_timeout, "default_auto");
|
||||||
return utest::v1::greentea_test_setup_handler(number_of_cases);
|
return utest::v1::greentea_test_setup_handler(number_of_cases);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,8 @@ Case cases[] = {
|
||||||
Case("1 s delay during deepsleep (attach_us)", test_deepsleep<AttachUSTester<LowPowerTimeout>, 1000000, LONG_DELTA_US>,
|
Case("1 s delay during deepsleep (attach_us)", test_deepsleep<AttachUSTester<LowPowerTimeout>, 1000000, LONG_DELTA_US>,
|
||||||
greentea_failure_handler),
|
greentea_failure_handler),
|
||||||
#endif
|
#endif
|
||||||
#if !defined(__ARM_FM) //FastModels not support time drifting test
|
|
||||||
|
#if !defined(SKIP_TIME_DRIFT_TESTS)
|
||||||
Case("Timing drift (attach)", test_drift<AttachTester<LowPowerTimeout> >),
|
Case("Timing drift (attach)", test_drift<AttachTester<LowPowerTimeout> >),
|
||||||
Case("Timing drift (attach_us)", test_drift<AttachUSTester<LowPowerTimeout> >),
|
Case("Timing drift (attach_us)", test_drift<AttachUSTester<LowPowerTimeout> >),
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MBED_DRIVERS_RESET_REASON_TESTS_H
|
||||||
|
#define MBED_DRIVERS_RESET_REASON_TESTS_H
|
||||||
|
|
||||||
|
#if DEVICE_RESET_REASON
|
||||||
|
|
||||||
|
/** Test the ResetReason driver API
|
||||||
|
*
|
||||||
|
* Given a device supporting a ResetReason API,
|
||||||
|
* when the device is restarted,
|
||||||
|
* then the device returns a correct reset reason for every restart.
|
||||||
|
*/
|
||||||
|
void test_reset_reason();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#if !DEVICE_RESET_REASON
|
||||||
|
#error [NOT_SUPPORTED] Reset reason API not supported for this target
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "greentea-client/test_env.h"
|
||||||
|
#include "unity/unity.h"
|
||||||
|
#include "drivers/ResetReason.h"
|
||||||
|
#include "ResetReason_tests.h"
|
||||||
|
#include "mbed.h"
|
||||||
|
|
||||||
|
#if DEVICE_WATCHDOG
|
||||||
|
#include "hal/watchdog_api.h"
|
||||||
|
|
||||||
|
#define MSG_VALUE_WATCHDOG_STATUS "wdg_present"
|
||||||
|
#define WDG_TIMEOUT_MS 50UL
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define MSG_VALUE_WATCHDOG_STATUS "no_wdg"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MSG_VALUE_DUMMY "0"
|
||||||
|
#define MSG_VALUE_RESET_REASON_GET "get"
|
||||||
|
#define MSG_VALUE_RESET_REASON_CLEAR "clear"
|
||||||
|
#define MSG_VALUE_RESET_REASON_CLEAR_ACK "cleared"
|
||||||
|
#define MSG_VALUE_DEVICE_RESET_ACK "ack"
|
||||||
|
#define MSG_VALUE_DEVICE_RESET_NVIC "nvic"
|
||||||
|
#define MSG_VALUE_DEVICE_RESET_WATCHDOG "watchdog"
|
||||||
|
#define MSG_VALUE_LEN 16
|
||||||
|
#define MSG_KEY_LEN 16
|
||||||
|
|
||||||
|
#define MSG_KEY_DEVICE_READY "ready"
|
||||||
|
#define MSG_KEY_RESET_REASON_RAW "reason_raw"
|
||||||
|
#define MSG_KEY_RESET_REASON "reason"
|
||||||
|
#define MSG_KEY_DEVICE_RESET "reset"
|
||||||
|
|
||||||
|
#define SERIAL_FLUSH_TIME_MS 20
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CMD_STATUS_CONTINUE,
|
||||||
|
CMD_STATUS_ERROR
|
||||||
|
} cmd_status_t;
|
||||||
|
|
||||||
|
static cmd_status_t handle_command(const char *key, const char *value)
|
||||||
|
{
|
||||||
|
if (strcmp(key, MSG_KEY_RESET_REASON_RAW) == 0) {
|
||||||
|
uint32_t raw_reason = ResetReason::get_raw();
|
||||||
|
char raw_reason_hex_str[9] = { };
|
||||||
|
int raw_reason_hex_str_len = snprintf(raw_reason_hex_str,
|
||||||
|
sizeof raw_reason_hex_str, "%08lx", raw_reason);
|
||||||
|
|
||||||
|
if (raw_reason_hex_str_len != (sizeof raw_reason_hex_str) - 1) {
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Failed to compose raw reset reason hex string.");
|
||||||
|
return CMD_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
greentea_send_kv(MSG_KEY_RESET_REASON_RAW, raw_reason_hex_str);
|
||||||
|
return CMD_STATUS_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(key, MSG_KEY_RESET_REASON) == 0 && strcmp(value, MSG_VALUE_RESET_REASON_GET) == 0) {
|
||||||
|
int reason = (int) ResetReason::get();
|
||||||
|
greentea_send_kv(MSG_KEY_RESET_REASON, reason);
|
||||||
|
return CMD_STATUS_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(key, MSG_KEY_RESET_REASON) == 0 && strcmp(value, MSG_VALUE_RESET_REASON_CLEAR) == 0) {
|
||||||
|
/* In order to keep this code compatible with a host script common for
|
||||||
|
* both HAL API tests and driver API tests, ignore the 'clear' command
|
||||||
|
* received from host.
|
||||||
|
*
|
||||||
|
* The driver API does not provide clear() function directly.
|
||||||
|
*/
|
||||||
|
greentea_send_kv(MSG_KEY_RESET_REASON, MSG_VALUE_RESET_REASON_CLEAR_ACK);
|
||||||
|
return CMD_STATUS_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(key, MSG_KEY_DEVICE_RESET) == 0 && strcmp(value, MSG_VALUE_DEVICE_RESET_NVIC) == 0) {
|
||||||
|
greentea_send_kv(MSG_KEY_DEVICE_RESET, MSG_VALUE_DEVICE_RESET_ACK);
|
||||||
|
wait_ms(SERIAL_FLUSH_TIME_MS); // Wait for the serial buffers to flush.
|
||||||
|
NVIC_SystemReset();
|
||||||
|
TEST_ASSERT_MESSAGE(0, "NVIC_SystemReset did not reset the device as expected.");
|
||||||
|
return CMD_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEVICE_WATCHDOG
|
||||||
|
if (strcmp(key, MSG_KEY_DEVICE_RESET) == 0 && strcmp(value, MSG_VALUE_DEVICE_RESET_WATCHDOG) == 0) {
|
||||||
|
greentea_send_kv(MSG_KEY_DEVICE_RESET, MSG_VALUE_DEVICE_RESET_ACK);
|
||||||
|
wait_ms(SERIAL_FLUSH_TIME_MS); // Wait for the serial buffers to flush.
|
||||||
|
watchdog_config_t config = { .timeout_ms = WDG_TIMEOUT_MS };
|
||||||
|
if (hal_watchdog_init(&config) != WATCHDOG_STATUS_OK) {
|
||||||
|
TEST_ASSERT_MESSAGE(0, "hal_watchdog_init() error.");
|
||||||
|
return CMD_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
wait_ms(2 * WDG_TIMEOUT_MS); // Watchdog should fire before twice the timeout value.
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected.");
|
||||||
|
return CMD_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Invalid message key.");
|
||||||
|
return CMD_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_reset_reason()
|
||||||
|
{
|
||||||
|
// Report readiness and watchdog status.
|
||||||
|
greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_WATCHDOG_STATUS);
|
||||||
|
|
||||||
|
cmd_status_t cmd_status = CMD_STATUS_CONTINUE;
|
||||||
|
static char _key[MSG_KEY_LEN + 1] = { };
|
||||||
|
static char _value[MSG_VALUE_LEN + 1] = { };
|
||||||
|
|
||||||
|
// Let the host side decide what to do and just handle the commands.
|
||||||
|
while (CMD_STATUS_CONTINUE == cmd_status) {
|
||||||
|
memset(_key, 0, sizeof _key);
|
||||||
|
memset(_value, 0, sizeof _value);
|
||||||
|
greentea_parse_kv(_key, _value, MSG_KEY_LEN, MSG_VALUE_LEN);
|
||||||
|
cmd_status = handle_command(_key, _value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
GREENTEA_SETUP(60, "reset_reason");
|
||||||
|
test_reset_reason(); // The result of this test suite is reported by the host side.
|
||||||
|
GREENTEA_TESTSUITE_RESULT(0); // Fail on any error.
|
||||||
|
}
|
|
@ -71,7 +71,9 @@ void ticker_callback_1(void)
|
||||||
void ticker_callback_2(void)
|
void ticker_callback_2(void)
|
||||||
{
|
{
|
||||||
++callback_trigger_count;
|
++callback_trigger_count;
|
||||||
switch_led2_state();
|
if (LED2 != NC) {
|
||||||
|
switch_led2_state();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -110,7 +112,9 @@ void test_case_1x_ticker()
|
||||||
Ticker ticker;
|
Ticker ticker;
|
||||||
|
|
||||||
led1 = 1;
|
led1 = 1;
|
||||||
led2 = 1;
|
if (LED2 != NC) {
|
||||||
|
led2 = 1;
|
||||||
|
}
|
||||||
callback_trigger_count = 0;
|
callback_trigger_count = 0;
|
||||||
|
|
||||||
greentea_send_kv("timing_drift_check_start", 0);
|
greentea_send_kv("timing_drift_check_start", 0);
|
||||||
|
@ -151,7 +155,9 @@ void test_case_2x_ticker()
|
||||||
Ticker ticker1, ticker2;
|
Ticker ticker1, ticker2;
|
||||||
|
|
||||||
led1 = 0;
|
led1 = 0;
|
||||||
led2 = 1;
|
if (LED2 != NC) {
|
||||||
|
led2 = 1;
|
||||||
|
}
|
||||||
callback_trigger_count = 0;
|
callback_trigger_count = 0;
|
||||||
|
|
||||||
ticker1.attach_us(ticker_callback_1, 2 * ONE_MILLI_SEC);
|
ticker1.attach_us(ticker_callback_1, 2 * ONE_MILLI_SEC);
|
||||||
|
@ -329,7 +335,8 @@ Case cases[] = {
|
||||||
Case("Test detach", test_detach),
|
Case("Test detach", test_detach),
|
||||||
Case("Test multi call and time measure", test_multi_call_time),
|
Case("Test multi call and time measure", test_multi_call_time),
|
||||||
Case("Test multi ticker", test_multi_ticker),
|
Case("Test multi ticker", test_multi_ticker),
|
||||||
#if !defined(__ARM_FM) //FastModels not support time drifting test
|
|
||||||
|
#if !defined(SKIP_TIME_DRIFT_TESTS)
|
||||||
Case("Test timers: 1x ticker", test_case_1x_ticker),
|
Case("Test timers: 1x ticker", test_case_1x_ticker),
|
||||||
Case("Test timers: 2x ticker", test_case_2x_ticker)
|
Case("Test timers: 2x ticker", test_case_2x_ticker)
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -68,7 +68,8 @@ Case cases[] = {
|
||||||
Case("1 s delay during sleep (attach_us)", test_sleep<AttachUSTester<Timeout>, 1000000, LONG_DELTA_US>,
|
Case("1 s delay during sleep (attach_us)", test_sleep<AttachUSTester<Timeout>, 1000000, LONG_DELTA_US>,
|
||||||
greentea_failure_handler),
|
greentea_failure_handler),
|
||||||
#endif
|
#endif
|
||||||
#if !defined(__ARM_FM) //FastModels not support time drifting test
|
|
||||||
|
#if !defined(SKIP_TIME_DRIFT_TESTS)
|
||||||
Case("Timing drift (attach)", test_drift<AttachTester<Timeout> >),
|
Case("Timing drift (attach)", test_drift<AttachTester<Timeout> >),
|
||||||
Case("Timing drift (attach_us)", test_drift<AttachUSTester<Timeout> >),
|
Case("Timing drift (attach_us)", test_drift<AttachUSTester<Timeout> >),
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -196,13 +196,14 @@ void test_multiple(void)
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void test_no_wait(void)
|
void test_no_wait(void)
|
||||||
{
|
{
|
||||||
Semaphore sem(0, 1);
|
for (int i = 0; i < 100; i++) {
|
||||||
T timeout;
|
Semaphore sem(0, 1);
|
||||||
timeout.attach_callback(mbed::callback(sem_callback, &sem), 0ULL);
|
T timeout;
|
||||||
|
timeout.attach_callback(mbed::callback(sem_callback, &sem), 0ULL);
|
||||||
bool acquired = sem.try_acquire();
|
int32_t sem_slots = sem.wait(0);
|
||||||
TEST_ASSERT_TRUE(acquired);
|
TEST_ASSERT_EQUAL(1, sem_slots);
|
||||||
timeout.detach();
|
timeout.detach();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Template for tests: accuracy of timeout delay
|
/** Template for tests: accuracy of timeout delay
|
||||||
|
@ -264,9 +265,7 @@ void test_sleep(void)
|
||||||
|
|
||||||
bool deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check();
|
bool deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check();
|
||||||
TEST_ASSERT_FALSE_MESSAGE(deep_sleep_allowed, "Deep sleep should be disallowed");
|
TEST_ASSERT_FALSE_MESSAGE(deep_sleep_allowed, "Deep sleep should be disallowed");
|
||||||
while (!sem.try_acquire()) {
|
sem.acquire();
|
||||||
sleep();
|
|
||||||
}
|
|
||||||
timer.stop();
|
timer.stop();
|
||||||
|
|
||||||
sleep_manager_unlock_deep_sleep();
|
sleep_manager_unlock_deep_sleep();
|
||||||
|
@ -323,9 +322,7 @@ void test_deepsleep(void)
|
||||||
|
|
||||||
bool deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check();
|
bool deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check();
|
||||||
TEST_ASSERT_TRUE_MESSAGE(deep_sleep_allowed, "Deep sleep should be allowed");
|
TEST_ASSERT_TRUE_MESSAGE(deep_sleep_allowed, "Deep sleep should be allowed");
|
||||||
while (!sem.try_acquire()) {
|
sem.acquire();
|
||||||
sleep();
|
|
||||||
}
|
|
||||||
timer.stop();
|
timer.stop();
|
||||||
|
|
||||||
TEST_ASSERT_UINT64_WITHIN(delta_us, delay_us, timer.read_high_resolution_us());
|
TEST_ASSERT_UINT64_WITHIN(delta_us, delay_us, timer.read_high_resolution_us());
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MBED_DRIVERS_WATCHDOG_TESTS_H
|
||||||
|
#define MBED_DRIVERS_WATCHDOG_TESTS_H
|
||||||
|
|
||||||
|
#if DEVICE_WATCHDOG
|
||||||
|
|
||||||
|
/** Test Watchdog max_timeout validity
|
||||||
|
*
|
||||||
|
* Given a device supporting Watchdog driver API,
|
||||||
|
* when @a Watchdog::get_max_timeout() is called,
|
||||||
|
* then the returned value is greater than 1.
|
||||||
|
*/
|
||||||
|
void test_max_timeout_is_valid();
|
||||||
|
|
||||||
|
/** Test Watchdog stop
|
||||||
|
*
|
||||||
|
* Given a device without a support for the @a disable_watchdog feature,
|
||||||
|
* when @a Watchdog::stop() is called,
|
||||||
|
* then false is returned.
|
||||||
|
*
|
||||||
|
* Otherwise, given the device with @a disable_watchdog feature support:
|
||||||
|
*
|
||||||
|
* Given the Watchdog is *NOT* running,
|
||||||
|
* when @a Watchdog::stop() is called,
|
||||||
|
* then false is returned.
|
||||||
|
*
|
||||||
|
* Given the Watchdog is running,
|
||||||
|
* when @a Watchdog::stop() is called before the timeout expires,
|
||||||
|
* then true is returned and the device is not restarted.
|
||||||
|
*
|
||||||
|
* Given the Watchdog is *NOT* running (it has already been stopped),
|
||||||
|
* when @a Watchdog::stop() is called,
|
||||||
|
* then false is returned.
|
||||||
|
*/
|
||||||
|
void test_stop();
|
||||||
|
|
||||||
|
/** Test Watchdog start multiple times
|
||||||
|
*
|
||||||
|
* Given a set of unique timeout values,
|
||||||
|
* when Watchdog::start(T) is called for each value T,
|
||||||
|
* then, for every T, Watchdog::start() returns true
|
||||||
|
* and Watchdog::get_timeout() returns an actual timeout value R
|
||||||
|
* and T <= R < 2 * T.
|
||||||
|
*/
|
||||||
|
void test_restart();
|
||||||
|
|
||||||
|
/** Test Watchdog start with a valid config
|
||||||
|
*
|
||||||
|
* Given a value of T ms which is within supported Watchdog timeout range,
|
||||||
|
* when Watchdog::start(T) is called,
|
||||||
|
* then true is returned
|
||||||
|
* and Watchdog::get_timeout() returns an actual timeout value R
|
||||||
|
* and T <= R < 2 * T.
|
||||||
|
*/
|
||||||
|
template<uint32_t timeout_ms>
|
||||||
|
void test_start();
|
||||||
|
|
||||||
|
/** Test Watchdog start with max_timeout
|
||||||
|
*
|
||||||
|
* Given max_timeout value returned by @a Watchdog::get_max_timeout(),
|
||||||
|
* when @a Watchdog::start(max_timeout) is called,
|
||||||
|
* then true is returned
|
||||||
|
* and @a Watchdog::get_timeout() returns max_timeout.
|
||||||
|
*/
|
||||||
|
void test_start_max_timeout();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,284 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#if !DEVICE_WATCHDOG
|
||||||
|
#error [NOT_SUPPORTED] Watchdog not supported for this target
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "greentea-client/test_env.h"
|
||||||
|
#include "unity/unity.h"
|
||||||
|
#include "utest/utest.h"
|
||||||
|
#include "mbed.h"
|
||||||
|
#include "drivers/Watchdog.h"
|
||||||
|
#include "hal/watchdog_api.h"
|
||||||
|
#include "Watchdog_tests.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* The shortest timeout value, this test suite is able to handle correctly. */
|
||||||
|
#define WDG_MIN_TIMEOUT_MS 50UL
|
||||||
|
|
||||||
|
// Do not set watchdog timeout shorter than WDG_MIN_TIMEOUT_MS, as it may
|
||||||
|
// cause the host-test-runner return 'TIMEOUT' instead of 'FAIL' / 'PASS'
|
||||||
|
// if watchdog performs reset during test suite teardown.
|
||||||
|
#define WDG_TIMEOUT_MS 100UL
|
||||||
|
|
||||||
|
#define MSG_VALUE_DUMMY "0"
|
||||||
|
#define MSG_VALUE_LEN 24
|
||||||
|
#define MSG_KEY_LEN 24
|
||||||
|
|
||||||
|
#define MSG_KEY_DEVICE_READY "ready"
|
||||||
|
#define MSG_KEY_START_CASE "start_case"
|
||||||
|
#define MSG_KEY_DEVICE_RESET "reset_on_case_teardown"
|
||||||
|
|
||||||
|
/* To prevent a loss of Greentea data, the serial buffers have to be flushed
|
||||||
|
* before the UART peripheral shutdown. The UART shutdown happens when the
|
||||||
|
* device is entering the deepsleep mode or performing a reset.
|
||||||
|
*
|
||||||
|
* With the current API, it is not possible to check if the hardware buffers
|
||||||
|
* are empty. However, it is possible to determine the time required for the
|
||||||
|
* buffers to flush.
|
||||||
|
*
|
||||||
|
* Take NUMAKER_PFM_NUC472 as an example:
|
||||||
|
* The UART peripheral has 16-byte Tx FIFO. With a baud rate set to 9600,
|
||||||
|
* flushing the Tx FIFO would take: 16 * 8 * 1000 / 9600 = 13.3 ms.
|
||||||
|
* To be on the safe side, set the wait time to 20 ms.
|
||||||
|
*/
|
||||||
|
#define SERIAL_FLUSH_TIME_MS 20
|
||||||
|
|
||||||
|
int CASE_INDEX_START;
|
||||||
|
int CASE_INDEX_CURRENT;
|
||||||
|
bool CASE_IGNORED = false;
|
||||||
|
|
||||||
|
using utest::v1::Case;
|
||||||
|
using utest::v1::Specification;
|
||||||
|
using utest::v1::Harness;
|
||||||
|
|
||||||
|
using namespace mbed;
|
||||||
|
|
||||||
|
Thread wdg_kicking_thread;
|
||||||
|
Semaphore kick_wdg_during_test_teardown(0, 1);
|
||||||
|
|
||||||
|
void wdg_kicking_thread_fun()
|
||||||
|
{
|
||||||
|
kick_wdg_during_test_teardown.wait();
|
||||||
|
while (true) {
|
||||||
|
hal_watchdog_kick();
|
||||||
|
wait_ms(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_max_timeout_is_valid()
|
||||||
|
{
|
||||||
|
Watchdog &watchdog = Watchdog::get_instance();
|
||||||
|
TEST_ASSERT(watchdog.get_max_timeout() > 1UL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_stop()
|
||||||
|
{
|
||||||
|
watchdog_features_t features = hal_watchdog_get_platform_features();
|
||||||
|
Watchdog &watchdog = Watchdog::get_instance();
|
||||||
|
if (!features.disable_watchdog) {
|
||||||
|
TEST_ASSERT_FALSE(watchdog.stop());
|
||||||
|
CASE_IGNORED = true;
|
||||||
|
TEST_IGNORE_MESSAGE("Disabling watchdog not supported for this platform");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_ASSERT_FALSE(watchdog.is_running());
|
||||||
|
TEST_ASSERT_FALSE(watchdog.stop());
|
||||||
|
TEST_ASSERT_FALSE(watchdog.is_running());
|
||||||
|
|
||||||
|
TEST_ASSERT_TRUE(watchdog.start(WDG_TIMEOUT_MS));
|
||||||
|
TEST_ASSERT_TRUE(watchdog.is_running());
|
||||||
|
TEST_ASSERT_TRUE(watchdog.stop());
|
||||||
|
TEST_ASSERT_FALSE(watchdog.is_running());
|
||||||
|
// Make sure that a disabled watchdog does not reset the core.
|
||||||
|
wait_ms(2 * WDG_TIMEOUT_MS); // Watchdog should fire before twice the timeout value.
|
||||||
|
|
||||||
|
TEST_ASSERT_FALSE(watchdog.stop());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void test_restart()
|
||||||
|
{
|
||||||
|
watchdog_features_t features = hal_watchdog_get_platform_features();
|
||||||
|
if (!features.update_config) {
|
||||||
|
CASE_IGNORED = true;
|
||||||
|
TEST_IGNORE_MESSAGE("Updating watchdog config not supported for this platform");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!features.disable_watchdog) {
|
||||||
|
CASE_IGNORED = true;
|
||||||
|
TEST_IGNORE_MESSAGE("Disabling watchdog not supported for this platform");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Watchdog &watchdog = Watchdog::get_instance();
|
||||||
|
uint32_t max_timeout = watchdog.get_max_timeout();
|
||||||
|
uint32_t timeouts[] = {
|
||||||
|
max_timeout / 4,
|
||||||
|
max_timeout / 8,
|
||||||
|
max_timeout / 16
|
||||||
|
};
|
||||||
|
int num_timeouts = sizeof timeouts / sizeof timeouts[0];
|
||||||
|
|
||||||
|
for (size_t i = 0; i < num_timeouts; i++) {
|
||||||
|
if (timeouts[i] < WDG_MIN_TIMEOUT_MS) {
|
||||||
|
CASE_IGNORED = true;
|
||||||
|
TEST_IGNORE_MESSAGE("Requested timeout value is too short -- ignoring test case.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_ASSERT_TRUE(watchdog.start(timeouts[i]));
|
||||||
|
TEST_ASSERT_TRUE(watchdog.is_running());
|
||||||
|
uint32_t actual_timeout = watchdog.get_timeout();
|
||||||
|
TEST_ASSERT_TRUE(watchdog.stop());
|
||||||
|
TEST_ASSERT_FALSE(watchdog.is_running());
|
||||||
|
// The watchdog should trigger at, or after the timeout value.
|
||||||
|
TEST_ASSERT(actual_timeout >= timeouts[i]);
|
||||||
|
// The watchdog should trigger before twice the timeout value.
|
||||||
|
TEST_ASSERT(actual_timeout < 2 * timeouts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
utest::v1::status_t case_setup_sync_on_reset(const Case *const source, const size_t index_of_case)
|
||||||
|
{
|
||||||
|
CASE_INDEX_CURRENT = index_of_case;
|
||||||
|
CASE_IGNORED = false;
|
||||||
|
return utest::v1::greentea_case_setup_handler(source, index_of_case);
|
||||||
|
}
|
||||||
|
|
||||||
|
utest::v1::status_t case_teardown_sync_on_reset(const Case *const source, const size_t passed, const size_t failed,
|
||||||
|
const utest::v1::failure_t failure)
|
||||||
|
{
|
||||||
|
if (CASE_IGNORED) {
|
||||||
|
return utest::v1::greentea_case_teardown_handler(source, passed, failed, failure);
|
||||||
|
}
|
||||||
|
// Unlock kicking the watchdog during teardown.
|
||||||
|
kick_wdg_during_test_teardown.release();
|
||||||
|
utest::v1::status_t status = utest::v1::greentea_case_teardown_handler(source, passed, failed, failure);
|
||||||
|
if (failed) {
|
||||||
|
/* Return immediately and skip the device reset, if the test case failed.
|
||||||
|
* Provided that the device won't be restarted by other means (i.e. watchdog timer),
|
||||||
|
* this should allow the test suite to finish in a defined manner
|
||||||
|
* and report failure to host.
|
||||||
|
* In case of watchdog reset during test suite teardown, the loss of serial
|
||||||
|
* connection is possible, so the host-test-runner may return 'TIMEOUT'
|
||||||
|
* instead of 'FAIL'.
|
||||||
|
*/
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
greentea_send_kv(MSG_KEY_DEVICE_RESET, CASE_INDEX_START + CASE_INDEX_CURRENT);
|
||||||
|
utest_printf("The device will now restart.\n");
|
||||||
|
wait_ms(SERIAL_FLUSH_TIME_MS); // Wait for the serial buffers to flush.
|
||||||
|
NVIC_SystemReset();
|
||||||
|
return status; // Reset is instant so this line won't be reached.
|
||||||
|
}
|
||||||
|
|
||||||
|
utest::v1::status_t case_teardown_wdg_stop_or_reset(const Case *const source, const size_t passed, const size_t failed,
|
||||||
|
const utest::v1::failure_t failure)
|
||||||
|
{
|
||||||
|
if (CASE_IGNORED) {
|
||||||
|
return utest::v1::greentea_case_teardown_handler(source, passed, failed, failure);
|
||||||
|
}
|
||||||
|
watchdog_features_t features = hal_watchdog_get_platform_features();
|
||||||
|
if (features.disable_watchdog) {
|
||||||
|
hal_watchdog_stop();
|
||||||
|
return utest::v1::greentea_case_teardown_handler(source, passed, failed, failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
return case_teardown_sync_on_reset(source, passed, failed, failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<uint32_t timeout_ms>
|
||||||
|
void test_start()
|
||||||
|
{
|
||||||
|
if (timeout_ms < WDG_MIN_TIMEOUT_MS) {
|
||||||
|
CASE_IGNORED = true;
|
||||||
|
TEST_IGNORE_MESSAGE("Requested timeout value is too short -- ignoring test case.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Watchdog &watchdog = Watchdog::get_instance();
|
||||||
|
TEST_ASSERT_TRUE(watchdog.start(timeout_ms));
|
||||||
|
uint32_t actual_timeout = watchdog.get_timeout();
|
||||||
|
// The watchdog should trigger at, or after the timeout value.
|
||||||
|
TEST_ASSERT(actual_timeout >= timeout_ms);
|
||||||
|
// The watchdog should trigger before twice the timeout value.
|
||||||
|
TEST_ASSERT(actual_timeout < 2 * timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_start_max_timeout()
|
||||||
|
{
|
||||||
|
Watchdog &watchdog = Watchdog::get_instance();
|
||||||
|
uint32_t max_timeout = watchdog.get_max_timeout();
|
||||||
|
TEST_ASSERT_TRUE(watchdog.start(max_timeout));
|
||||||
|
// The watchdog should trigger at, or after the timeout value.
|
||||||
|
TEST_ASSERT(watchdog.get_timeout() >= max_timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
int testsuite_setup_sync_on_reset(const size_t number_of_cases)
|
||||||
|
{
|
||||||
|
GREENTEA_SETUP(45, "sync_on_reset");
|
||||||
|
utest::v1::status_t status = utest::v1::greentea_test_setup_handler(number_of_cases);
|
||||||
|
if (status != utest::v1::STATUS_CONTINUE) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
char key[MSG_KEY_LEN + 1] = { };
|
||||||
|
char value[MSG_VALUE_LEN + 1] = { };
|
||||||
|
|
||||||
|
greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_DUMMY);
|
||||||
|
greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN);
|
||||||
|
|
||||||
|
if (strcmp(key, MSG_KEY_START_CASE) != 0) {
|
||||||
|
utest_printf("Invalid message key.\n");
|
||||||
|
return utest::v1::STATUS_ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *tailptr = NULL;
|
||||||
|
CASE_INDEX_START = (int) strtol(value, &tailptr, 10);
|
||||||
|
if (*tailptr != '\0' || CASE_INDEX_START < 0) {
|
||||||
|
utest_printf("Invalid start case index received from host\n");
|
||||||
|
return utest::v1::STATUS_ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The thread is started here, but feeding the watchdog will start
|
||||||
|
// when the semaphore is released during a test case teardown.
|
||||||
|
wdg_kicking_thread.start(mbed::callback(wdg_kicking_thread_fun));
|
||||||
|
|
||||||
|
utest_printf("Starting with test case index %i of all %i defined test cases.\n", CASE_INDEX_START, number_of_cases);
|
||||||
|
return CASE_INDEX_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
Case cases[] = {
|
||||||
|
Case("max_timeout is valid", test_max_timeout_is_valid),
|
||||||
|
Case("Stop", test_stop),
|
||||||
|
Case("Restart multiple times",
|
||||||
|
(utest::v1::case_setup_handler_t) case_setup_sync_on_reset,
|
||||||
|
test_restart, (utest::v1::case_teardown_handler_t) case_teardown_wdg_stop_or_reset),
|
||||||
|
Case("Start, 100 ms", (utest::v1::case_setup_handler_t) case_setup_sync_on_reset,
|
||||||
|
test_start<100UL>, (utest::v1::case_teardown_handler_t) case_teardown_sync_on_reset),
|
||||||
|
Case("Start, max_timeout", (utest::v1::case_setup_handler_t) case_setup_sync_on_reset,
|
||||||
|
test_start_max_timeout, (utest::v1::case_teardown_handler_t) case_teardown_sync_on_reset),
|
||||||
|
};
|
||||||
|
|
||||||
|
Specification specification((utest::v1::test_setup_handler_t) testsuite_setup_sync_on_reset, cases);
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Harness will start with a test case index provided by host script.
|
||||||
|
return !Harness::run(specification);
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MBED_DRIVERS_WATCHDOG_RESET_TESTS_H
|
||||||
|
#define MBED_DRIVERS_WATCHDOG_RESET_TESTS_H
|
||||||
|
|
||||||
|
#if DEVICE_WATCHDOG
|
||||||
|
|
||||||
|
/** Test Watchdog reset
|
||||||
|
*
|
||||||
|
* Given a device with a Watchdog started,
|
||||||
|
* when a Watchdog timeout expires,
|
||||||
|
* then the device is restarted.
|
||||||
|
*/
|
||||||
|
void test_simple_reset();
|
||||||
|
|
||||||
|
/** Test Watchdog reset in sleep mode
|
||||||
|
*
|
||||||
|
* Given a device with a Watchdog started,
|
||||||
|
* when the Watchdog timeout expires while the device is in sleep mode,
|
||||||
|
* then the device is restarted.
|
||||||
|
*/
|
||||||
|
void test_sleep_reset();
|
||||||
|
|
||||||
|
/** Test Watchdog reset in deepsleep mode
|
||||||
|
*
|
||||||
|
* Given a device with a Watchdog started,
|
||||||
|
* when the Watchdog timeout expires while the device is in deepsleep mode,
|
||||||
|
* then the device is restarted.
|
||||||
|
*/
|
||||||
|
void test_deepsleep_reset();
|
||||||
|
|
||||||
|
/** Test Watchdog reset after Watchdog restart
|
||||||
|
*
|
||||||
|
* Given a device with a Watchdog started,
|
||||||
|
* when the Watchdog is stopped before its timeout expires,
|
||||||
|
* then the device is not restarted.
|
||||||
|
* When the Watchdog is started again and its timeout expires,
|
||||||
|
* then the device is restarted.
|
||||||
|
*/
|
||||||
|
void test_restart_reset();
|
||||||
|
|
||||||
|
/** Test Watchdog kick
|
||||||
|
*
|
||||||
|
* Given a device with a Watchdog started,
|
||||||
|
* when the Watchdog is kicked before its timeout expires,
|
||||||
|
* then the device restart is prevented.
|
||||||
|
* When the Watchdog is *NOT* kicked again before next timeout expires,
|
||||||
|
* then the device is restarted.
|
||||||
|
*/
|
||||||
|
void test_kick_reset();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,321 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#if !DEVICE_WATCHDOG
|
||||||
|
#error [NOT_SUPPORTED] Watchdog not supported for this target
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "greentea-client/test_env.h"
|
||||||
|
#include "utest/utest.h"
|
||||||
|
#include "unity/unity.h"
|
||||||
|
#include "drivers/Watchdog.h"
|
||||||
|
#include "Watchdog_reset_tests.h"
|
||||||
|
#include "mbed.h"
|
||||||
|
|
||||||
|
#define TIMEOUT_MS 100UL
|
||||||
|
#define KICK_ADVANCE_MS 10UL
|
||||||
|
|
||||||
|
#define MSG_VALUE_DUMMY "0"
|
||||||
|
#define CASE_DATA_INVALID 0xffffffffUL
|
||||||
|
#define CASE_DATA_PHASE2_OK 0xfffffffeUL
|
||||||
|
|
||||||
|
#define MSG_VALUE_LEN 24
|
||||||
|
#define MSG_KEY_LEN 24
|
||||||
|
|
||||||
|
#define MSG_KEY_DEVICE_READY "ready"
|
||||||
|
#define MSG_KEY_START_CASE "start_case"
|
||||||
|
#define MSG_KEY_DEVICE_RESET "dev_reset"
|
||||||
|
|
||||||
|
/* To prevent a loss of Greentea data, the serial buffers have to be flushed
|
||||||
|
* before the UART peripheral shutdown. The UART shutdown happens when the
|
||||||
|
* device is entering the deepsleep mode or performing a reset.
|
||||||
|
*
|
||||||
|
* With the current API, it is not possible to check if the hardware buffers
|
||||||
|
* are empty. However, it is possible to determine the time required for the
|
||||||
|
* buffers to flush.
|
||||||
|
*
|
||||||
|
* Take NUMAKER_PFM_NUC472 as an example:
|
||||||
|
* The UART peripheral has 16-byte Tx FIFO. With a baud rate set to 9600,
|
||||||
|
* flushing the Tx FIFO would take: 16 * 8 * 1000 / 9600 = 13.3 ms.
|
||||||
|
* To be on the safe side, set the wait time to 20 ms.
|
||||||
|
*/
|
||||||
|
#define SERIAL_FLUSH_TIME_MS 20
|
||||||
|
|
||||||
|
using utest::v1::Case;
|
||||||
|
using utest::v1::Specification;
|
||||||
|
using utest::v1::Harness;
|
||||||
|
|
||||||
|
using namespace mbed;
|
||||||
|
|
||||||
|
struct testcase_data {
|
||||||
|
int index;
|
||||||
|
int start_index;
|
||||||
|
uint32_t received_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
void release_sem(Semaphore *sem)
|
||||||
|
{
|
||||||
|
sem->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
testcase_data current_case;
|
||||||
|
|
||||||
|
bool send_reset_notification(testcase_data *tcdata, uint32_t delay_ms)
|
||||||
|
{
|
||||||
|
char msg_value[12];
|
||||||
|
int str_len = snprintf(msg_value, sizeof msg_value, "%02x,%08lx", tcdata->start_index + tcdata->index, delay_ms);
|
||||||
|
if (str_len != (sizeof msg_value) - 1) {
|
||||||
|
utest_printf("Failed to compose a value string to be sent to host.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
greentea_send_kv(MSG_KEY_DEVICE_RESET, msg_value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_simple_reset()
|
||||||
|
{
|
||||||
|
// Phase 2. -- verify the test results.
|
||||||
|
// Verify if this test case passed based on data received from host.
|
||||||
|
if (current_case.received_data != CASE_DATA_INVALID) {
|
||||||
|
TEST_ASSERT_EQUAL(CASE_DATA_PHASE2_OK, current_case.received_data);
|
||||||
|
current_case.received_data = CASE_DATA_INVALID;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 1. -- run the test code.
|
||||||
|
// Init the watchdog and wait for a device reset.
|
||||||
|
if (send_reset_notification(¤t_case, 2 * TIMEOUT_MS) == false) {
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Dev-host communication error.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Watchdog &watchdog = Watchdog::get_instance();
|
||||||
|
TEST_ASSERT_FALSE(watchdog.is_running());
|
||||||
|
TEST_ASSERT_TRUE(watchdog.start(TIMEOUT_MS));
|
||||||
|
TEST_ASSERT_TRUE(watchdog.is_running());
|
||||||
|
// Watchdog should fire before twice the timeout value.
|
||||||
|
wait_ms(2 * TIMEOUT_MS); // Device reset expected.
|
||||||
|
|
||||||
|
// Watchdog reset should have occurred during wait_ms() above;
|
||||||
|
|
||||||
|
watchdog.kick(); // Just to buy some time for testsuite failure handling.
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEVICE_SLEEP
|
||||||
|
void test_sleep_reset()
|
||||||
|
{
|
||||||
|
// Phase 2. -- verify the test results.
|
||||||
|
if (current_case.received_data != CASE_DATA_INVALID) {
|
||||||
|
TEST_ASSERT_EQUAL(CASE_DATA_PHASE2_OK, current_case.received_data);
|
||||||
|
current_case.received_data = CASE_DATA_INVALID;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 1. -- run the test code.
|
||||||
|
Semaphore sem(0, 1);
|
||||||
|
Timeout timeout;
|
||||||
|
if (send_reset_notification(¤t_case, 2 * TIMEOUT_MS) == false) {
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Dev-host communication error.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Watchdog &watchdog = Watchdog::get_instance();
|
||||||
|
TEST_ASSERT_FALSE(watchdog.is_running());
|
||||||
|
TEST_ASSERT_TRUE(watchdog.start(TIMEOUT_MS));
|
||||||
|
TEST_ASSERT_TRUE(watchdog.is_running());
|
||||||
|
sleep_manager_lock_deep_sleep();
|
||||||
|
// Watchdog should fire before twice the timeout value.
|
||||||
|
timeout.attach_us(mbed::callback(release_sem, &sem), 1000ULL * (2 * TIMEOUT_MS));
|
||||||
|
if (sleep_manager_can_deep_sleep()) {
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Deepsleep should be disallowed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sem.wait(); // Device reset expected.
|
||||||
|
sleep_manager_unlock_deep_sleep();
|
||||||
|
|
||||||
|
// Watchdog reset should have occurred during sem.wait() (sleep) above;
|
||||||
|
|
||||||
|
watchdog.kick(); // Just to buy some time for testsuite failure handling.
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEVICE_LOWPOWERTIMER
|
||||||
|
void test_deepsleep_reset()
|
||||||
|
{
|
||||||
|
// Phase 2. -- verify the test results.
|
||||||
|
if (current_case.received_data != CASE_DATA_INVALID) {
|
||||||
|
TEST_ASSERT_EQUAL(CASE_DATA_PHASE2_OK, current_case.received_data);
|
||||||
|
current_case.received_data = CASE_DATA_INVALID;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 1. -- run the test code.
|
||||||
|
Semaphore sem(0, 1);
|
||||||
|
LowPowerTimeout lp_timeout;
|
||||||
|
if (send_reset_notification(¤t_case, 2 * TIMEOUT_MS) == false) {
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Dev-host communication error.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Watchdog &watchdog = Watchdog::get_instance();
|
||||||
|
TEST_ASSERT_FALSE(watchdog.is_running());
|
||||||
|
TEST_ASSERT_TRUE(watchdog.start(TIMEOUT_MS));
|
||||||
|
TEST_ASSERT_TRUE(watchdog.is_running());
|
||||||
|
// Watchdog should fire before twice the timeout value.
|
||||||
|
lp_timeout.attach_us(mbed::callback(release_sem, &sem), 1000ULL * (2 * TIMEOUT_MS));
|
||||||
|
wait_ms(SERIAL_FLUSH_TIME_MS); // Wait for the serial buffers to flush.
|
||||||
|
if (!sleep_manager_can_deep_sleep()) {
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Deepsleep should be allowed.");
|
||||||
|
}
|
||||||
|
sem.wait(); // Device reset expected.
|
||||||
|
|
||||||
|
// Watchdog reset should have occurred during sem.wait() (deepsleep) above;
|
||||||
|
|
||||||
|
watchdog.kick(); // Just to buy some time for testsuite failure handling.
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected.");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void test_restart_reset()
|
||||||
|
{
|
||||||
|
watchdog_features_t features = hal_watchdog_get_platform_features();
|
||||||
|
if (!features.disable_watchdog) {
|
||||||
|
TEST_IGNORE_MESSAGE("Disabling Watchdog not supported for this platform");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 2. -- verify the test results.
|
||||||
|
if (current_case.received_data != CASE_DATA_INVALID) {
|
||||||
|
TEST_ASSERT_EQUAL(CASE_DATA_PHASE2_OK, current_case.received_data);
|
||||||
|
current_case.received_data = CASE_DATA_INVALID;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 1. -- run the test code.
|
||||||
|
Watchdog &watchdog = Watchdog::get_instance();
|
||||||
|
TEST_ASSERT_FALSE(watchdog.is_running());
|
||||||
|
TEST_ASSERT_TRUE(watchdog.start(TIMEOUT_MS));
|
||||||
|
TEST_ASSERT_TRUE(watchdog.is_running());
|
||||||
|
wait_ms(TIMEOUT_MS / 2UL);
|
||||||
|
TEST_ASSERT_TRUE(watchdog.stop());
|
||||||
|
TEST_ASSERT_FALSE(watchdog.is_running());
|
||||||
|
// Check that stopping the Watchdog prevents a device reset.
|
||||||
|
// The watchdog should trigger at, or after the timeout value.
|
||||||
|
// The watchdog should trigger before twice the timeout value.
|
||||||
|
wait_ms(TIMEOUT_MS / 2UL + TIMEOUT_MS);
|
||||||
|
|
||||||
|
if (send_reset_notification(¤t_case, 2 * TIMEOUT_MS) == false) {
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Dev-host communication error.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TEST_ASSERT_TRUE(watchdog.start(TIMEOUT_MS));
|
||||||
|
TEST_ASSERT_TRUE(watchdog.is_running());
|
||||||
|
// Watchdog should fire before twice the timeout value.
|
||||||
|
wait_ms(2 * TIMEOUT_MS); // Device reset expected.
|
||||||
|
|
||||||
|
// Watchdog reset should have occurred during that wait() above;
|
||||||
|
|
||||||
|
watchdog.kick(); // Just to buy some time for testsuite failure handling.
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_kick_reset()
|
||||||
|
{
|
||||||
|
// Phase 2. -- verify the test results.
|
||||||
|
if (current_case.received_data != CASE_DATA_INVALID) {
|
||||||
|
TEST_ASSERT_EQUAL(CASE_DATA_PHASE2_OK, current_case.received_data);
|
||||||
|
current_case.received_data = CASE_DATA_INVALID;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 1. -- run the test code.
|
||||||
|
Watchdog &watchdog = Watchdog::get_instance();
|
||||||
|
TEST_ASSERT_FALSE(watchdog.is_running());
|
||||||
|
TEST_ASSERT_TRUE(watchdog.start(TIMEOUT_MS));
|
||||||
|
TEST_ASSERT_TRUE(watchdog.is_running());
|
||||||
|
for (int i = 3; i; i--) {
|
||||||
|
// The reset is prevented as long as the watchdog is kicked
|
||||||
|
// anytime before the timeout.
|
||||||
|
wait_ms(TIMEOUT_MS - KICK_ADVANCE_MS);
|
||||||
|
watchdog.kick();
|
||||||
|
}
|
||||||
|
if (send_reset_notification(¤t_case, 2 * TIMEOUT_MS) == false) {
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Dev-host communication error.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Watchdog should fire before twice the timeout value.
|
||||||
|
wait_ms(2 * TIMEOUT_MS); // Device reset expected.
|
||||||
|
|
||||||
|
// Watchdog reset should have occurred during that wait() above;
|
||||||
|
|
||||||
|
watchdog.kick(); // Just to buy some time for testsuite failure handling.
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
utest::v1::status_t case_setup(const Case *const source, const size_t index_of_case)
|
||||||
|
{
|
||||||
|
current_case.index = index_of_case;
|
||||||
|
return utest::v1::greentea_case_setup_handler(source, index_of_case);
|
||||||
|
}
|
||||||
|
|
||||||
|
int testsuite_setup(const size_t number_of_cases)
|
||||||
|
{
|
||||||
|
GREENTEA_SETUP(90, "watchdog_reset");
|
||||||
|
utest::v1::status_t status = utest::v1::greentea_test_setup_handler(number_of_cases);
|
||||||
|
if (status != utest::v1::STATUS_CONTINUE) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
char key[MSG_KEY_LEN + 1] = { };
|
||||||
|
char value[MSG_VALUE_LEN + 1] = { };
|
||||||
|
|
||||||
|
greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_DUMMY);
|
||||||
|
greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN);
|
||||||
|
|
||||||
|
if (strcmp(key, MSG_KEY_START_CASE) != 0) {
|
||||||
|
utest_printf("Invalid message key.\n");
|
||||||
|
return utest::v1::STATUS_ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int num_args = sscanf(value, "%02x,%08lx", &(current_case.start_index), &(current_case.received_data));
|
||||||
|
if (num_args == 0 || num_args == EOF) {
|
||||||
|
utest_printf("Invalid data received from host\n");
|
||||||
|
return utest::v1::STATUS_ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
utest_printf("This test suite is composed of %i test cases. Starting at index %i.\n", number_of_cases,
|
||||||
|
current_case.start_index);
|
||||||
|
return current_case.start_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
Case cases[] = {
|
||||||
|
Case("Watchdog reset", case_setup, test_simple_reset),
|
||||||
|
#if DEVICE_SLEEP
|
||||||
|
Case("Watchdog reset in sleep mode", case_setup, test_sleep_reset),
|
||||||
|
#if DEVICE_LOWPOWERTIMER
|
||||||
|
Case("Watchdog reset in deepsleep mode", case_setup, test_deepsleep_reset),
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
Case("Watchdog started again", case_setup, test_restart_reset),
|
||||||
|
Case("Kicking the Watchdog prevents reset", case_setup, test_kick_reset),
|
||||||
|
};
|
||||||
|
|
||||||
|
Specification specification((utest::v1::test_setup_handler_t) testsuite_setup, cases);
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Harness will start with a test case index provided by host script.
|
||||||
|
return !Harness::run(specification);
|
||||||
|
}
|
|
@ -30,12 +30,11 @@
|
||||||
#include "hal/lp_ticker_api.h"
|
#include "hal/lp_ticker_api.h"
|
||||||
#include "hal/mbed_lp_ticker_wrapper.h"
|
#include "hal/mbed_lp_ticker_wrapper.h"
|
||||||
|
|
||||||
#if !DEVICE_USTICKER
|
#if defined(SKIP_TIME_DRIFT_TESTS)
|
||||||
#error [NOT_SUPPORTED] test not supported
|
#error [NOT_SUPPORTED] test not supported
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//FastModels not support time drifting test
|
#if !DEVICE_USTICKER
|
||||||
#if defined(__ARM_FM)
|
|
||||||
#error [NOT_SUPPORTED] test not supported
|
#error [NOT_SUPPORTED] test not supported
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ Case cases[] = {
|
||||||
|
|
||||||
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
||||||
{
|
{
|
||||||
GREENTEA_SETUP(10, "timing_drift_auto");
|
GREENTEA_SETUP(10, "default_auto");
|
||||||
return utest::v1::greentea_test_setup_handler(number_of_cases);
|
return utest::v1::greentea_test_setup_handler(number_of_cases);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define QUAD_IO_ENABLE() \
|
#define EXTENDED_SPI_ENABLE() \
|
||||||
\
|
\
|
||||||
const int32_t reg_size = QSPI_STATUS_REG_SIZE + QSPI_CONFIG_REG_0_SIZE; \
|
const int32_t reg_size = QSPI_STATUS_REG_SIZE + QSPI_CONFIG_REG_0_SIZE; \
|
||||||
uint8_t reg_data[reg_size] = { 0 }; \
|
uint8_t reg_data[reg_size] = { 0 }; \
|
||||||
|
@ -150,7 +150,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define QUAD_IO_DISABLE() \
|
#define EXTENDED_SPI_DISABLE() \
|
||||||
\
|
\
|
||||||
const int32_t reg_size = QSPI_STATUS_REG_SIZE + QSPI_CONFIG_REG_0_SIZE; \
|
const int32_t reg_size = QSPI_STATUS_REG_SIZE + QSPI_CONFIG_REG_0_SIZE; \
|
||||||
uint8_t reg_data[reg_size] = { 0 }; \
|
uint8_t reg_data[reg_size] = { 0 }; \
|
||||||
|
|
|
@ -26,8 +26,6 @@
|
||||||
#include "flash_configs/flash_configs.h"
|
#include "flash_configs/flash_configs.h"
|
||||||
#include "mbed.h"
|
#include "mbed.h"
|
||||||
|
|
||||||
static qspi_status_t quad_io_enable(Qspi &qspi);
|
|
||||||
static qspi_status_t quad_io_disable(Qspi &qspi);
|
|
||||||
static qspi_status_t extended_enable(Qspi &qspi);
|
static qspi_status_t extended_enable(Qspi &qspi);
|
||||||
static qspi_status_t extended_disable(Qspi &qspi);
|
static qspi_status_t extended_disable(Qspi &qspi);
|
||||||
static qspi_status_t dual_enable(Qspi &qspi);
|
static qspi_status_t dual_enable(Qspi &qspi);
|
||||||
|
@ -203,9 +201,7 @@ qspi_status_t erase(uint32_t erase_cmd, uint32_t flash_addr, Qspi &qspi)
|
||||||
|
|
||||||
qspi_status_t mode_enable(Qspi &qspi, qspi_bus_width_t inst_width, qspi_bus_width_t addr_width, qspi_bus_width_t data_width)
|
qspi_status_t mode_enable(Qspi &qspi, qspi_bus_width_t inst_width, qspi_bus_width_t addr_width, qspi_bus_width_t data_width)
|
||||||
{
|
{
|
||||||
if (is_quad_io_mode(inst_width, addr_width, data_width)) {
|
if (is_extended_mode(inst_width, addr_width, data_width)) {
|
||||||
return quad_io_enable(qspi);
|
|
||||||
} else if (is_extended_mode(inst_width, addr_width, data_width)) {
|
|
||||||
return extended_enable(qspi);
|
return extended_enable(qspi);
|
||||||
} else if (is_dual_mode(inst_width, addr_width, data_width)) {
|
} else if (is_dual_mode(inst_width, addr_width, data_width)) {
|
||||||
return dual_enable(qspi);
|
return dual_enable(qspi);
|
||||||
|
@ -218,9 +214,7 @@ qspi_status_t mode_enable(Qspi &qspi, qspi_bus_width_t inst_width, qspi_bus_widt
|
||||||
|
|
||||||
qspi_status_t mode_disable(Qspi &qspi, qspi_bus_width_t inst_width, qspi_bus_width_t addr_width, qspi_bus_width_t data_width)
|
qspi_status_t mode_disable(Qspi &qspi, qspi_bus_width_t inst_width, qspi_bus_width_t addr_width, qspi_bus_width_t data_width)
|
||||||
{
|
{
|
||||||
if (is_quad_io_mode(inst_width, addr_width, data_width)) {
|
if (is_extended_mode(inst_width, addr_width, data_width)) {
|
||||||
return quad_io_disable(qspi);
|
|
||||||
} else if (is_extended_mode(inst_width, addr_width, data_width)) {
|
|
||||||
return extended_disable(qspi);
|
return extended_disable(qspi);
|
||||||
} else if (is_dual_mode(inst_width, addr_width, data_width)) {
|
} else if (is_dual_mode(inst_width, addr_width, data_width)) {
|
||||||
return dual_disable(qspi);
|
return dual_disable(qspi);
|
||||||
|
@ -231,24 +225,6 @@ qspi_status_t mode_disable(Qspi &qspi, qspi_bus_width_t inst_width, qspi_bus_wid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static qspi_status_t quad_io_enable(Qspi &qspi)
|
|
||||||
{
|
|
||||||
#ifdef QUAD_IO_ENABLE
|
|
||||||
QUAD_IO_ENABLE();
|
|
||||||
#else
|
|
||||||
return QSPI_STATUS_OK;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static qspi_status_t quad_io_disable(Qspi &qspi)
|
|
||||||
{
|
|
||||||
#ifdef QUAD_IO_DISABLE
|
|
||||||
QUAD_IO_DISABLE();
|
|
||||||
#else
|
|
||||||
return QSPI_STATUS_OK;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static qspi_status_t extended_enable(Qspi &qspi)
|
static qspi_status_t extended_enable(Qspi &qspi)
|
||||||
{
|
{
|
||||||
#ifdef EXTENDED_SPI_ENABLE
|
#ifdef EXTENDED_SPI_ENABLE
|
||||||
|
@ -322,11 +298,6 @@ qspi_status_t fast_mode_disable(Qspi &qspi)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_quad_io_mode(qspi_bus_width_t inst_width, qspi_bus_width_t addr_width, qspi_bus_width_t data_width)
|
|
||||||
{
|
|
||||||
return (inst_width == QSPI_CFG_BUS_SINGLE) && ((addr_width == QSPI_CFG_BUS_QUAD) || (data_width == QSPI_CFG_BUS_QUAD));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_extended_mode(qspi_bus_width_t inst_width, qspi_bus_width_t addr_width, qspi_bus_width_t data_width)
|
bool is_extended_mode(qspi_bus_width_t inst_width, qspi_bus_width_t addr_width, qspi_bus_width_t data_width)
|
||||||
{
|
{
|
||||||
return (inst_width == QSPI_CFG_BUS_SINGLE) && ((addr_width != QSPI_CFG_BUS_SINGLE) || (data_width != QSPI_CFG_BUS_SINGLE));
|
return (inst_width == QSPI_CFG_BUS_SINGLE) && ((addr_width != QSPI_CFG_BUS_SINGLE) || (data_width != QSPI_CFG_BUS_SINGLE));
|
||||||
|
|
|
@ -150,7 +150,6 @@ qspi_status_t fast_mode_disable(Qspi &qspi);
|
||||||
|
|
||||||
qspi_status_t erase(uint32_t erase_cmd, uint32_t flash_addr, Qspi &qspi);
|
qspi_status_t erase(uint32_t erase_cmd, uint32_t flash_addr, Qspi &qspi);
|
||||||
|
|
||||||
bool is_quad_io_mode(qspi_bus_width_t inst_width, qspi_bus_width_t addr_width, qspi_bus_width_t data_width);
|
|
||||||
bool is_extended_mode(qspi_bus_width_t inst_width, qspi_bus_width_t addr_width, qspi_bus_width_t data_width);
|
bool is_extended_mode(qspi_bus_width_t inst_width, qspi_bus_width_t addr_width, qspi_bus_width_t data_width);
|
||||||
bool is_dual_mode(qspi_bus_width_t inst_width, qspi_bus_width_t addr_width, qspi_bus_width_t data_width);
|
bool is_dual_mode(qspi_bus_width_t inst_width, qspi_bus_width_t addr_width, qspi_bus_width_t data_width);
|
||||||
bool is_quad_mode(qspi_bus_width_t inst_width, qspi_bus_width_t addr_width, qspi_bus_width_t data_width);
|
bool is_quad_mode(qspi_bus_width_t inst_width, qspi_bus_width_t addr_width, qspi_bus_width_t data_width);
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#if !DEVICE_RESET_REASON
|
||||||
|
#error [NOT_SUPPORTED] Reset reason API not supported for this target
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "greentea-client/test_env.h"
|
||||||
|
#include "unity/unity.h"
|
||||||
|
#include "hal/reset_reason_api.h"
|
||||||
|
#include "reset_reason_api_tests.h"
|
||||||
|
#include "mbed.h"
|
||||||
|
|
||||||
|
#if DEVICE_WATCHDOG
|
||||||
|
#include "hal/watchdog_api.h"
|
||||||
|
|
||||||
|
#define MSG_VALUE_WATCHDOG_STATUS "wdg_present"
|
||||||
|
#define WDG_TIMEOUT_MS 50UL
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define MSG_VALUE_WATCHDOG_STATUS "no_wdg"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MSG_VALUE_DUMMY "0"
|
||||||
|
#define MSG_VALUE_RESET_REASON_GET "get"
|
||||||
|
#define MSG_VALUE_RESET_REASON_CLEAR "clear"
|
||||||
|
#define MSG_VALUE_RESET_REASON_CLEAR_ACK "cleared"
|
||||||
|
#define MSG_VALUE_DEVICE_RESET_ACK "ack"
|
||||||
|
#define MSG_VALUE_DEVICE_RESET_NVIC "nvic"
|
||||||
|
#define MSG_VALUE_DEVICE_RESET_WATCHDOG "watchdog"
|
||||||
|
#define MSG_VALUE_LEN 16
|
||||||
|
#define MSG_KEY_LEN 16
|
||||||
|
|
||||||
|
#define MSG_KEY_DEVICE_READY "ready"
|
||||||
|
#define MSG_KEY_RESET_REASON_RAW "reason_raw"
|
||||||
|
#define MSG_KEY_RESET_REASON "reason"
|
||||||
|
#define MSG_KEY_DEVICE_RESET "reset"
|
||||||
|
|
||||||
|
/* To prevent loss of Greentea data, flush serial buffers before the UART peripheral shutdown. The UART shutdown happens when the
|
||||||
|
* device is entering the deepsleep mode or performing a reset.
|
||||||
|
*
|
||||||
|
* With the current API, it is not possible to check if the hardware buffers
|
||||||
|
* are empty. However, you can determine the amount of time required for the
|
||||||
|
* buffers to flush.
|
||||||
|
*
|
||||||
|
* For example, NUMAKER_PFM_NUC472:
|
||||||
|
* The UART peripheral has 16-byte Tx FIFO. With a baud rate of 9600,
|
||||||
|
* flushing the Tx FIFO would take: 16 * 8 * 1000 / 9600 = 13.3 ms.
|
||||||
|
* To be on the safe side, set the wait time to 20 ms.
|
||||||
|
*/
|
||||||
|
#define SERIAL_FLUSH_TIME_MS 20
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CMD_STATUS_CONTINUE,
|
||||||
|
CMD_STATUS_ERROR
|
||||||
|
} cmd_status_t;
|
||||||
|
|
||||||
|
static cmd_status_t handle_command(const char *key, const char *value)
|
||||||
|
{
|
||||||
|
if (strcmp(key, MSG_KEY_RESET_REASON_RAW) == 0) {
|
||||||
|
uint32_t raw_reason = hal_reset_reason_get_raw();
|
||||||
|
char raw_reason_hex_str[9] = { };
|
||||||
|
int raw_reason_hex_str_len = snprintf(raw_reason_hex_str,
|
||||||
|
sizeof raw_reason_hex_str, "%08lx", raw_reason);
|
||||||
|
|
||||||
|
if (raw_reason_hex_str_len != (sizeof raw_reason_hex_str) - 1) {
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Failed to compose raw reset reason hex string.");
|
||||||
|
return CMD_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
greentea_send_kv(MSG_KEY_RESET_REASON_RAW, raw_reason_hex_str);
|
||||||
|
return CMD_STATUS_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(key, MSG_KEY_RESET_REASON) == 0 && strcmp(value, MSG_VALUE_RESET_REASON_GET) == 0) {
|
||||||
|
int reason = (int) hal_reset_reason_get();
|
||||||
|
greentea_send_kv(MSG_KEY_RESET_REASON, reason);
|
||||||
|
return CMD_STATUS_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(key, MSG_KEY_RESET_REASON) == 0 && strcmp(value, MSG_VALUE_RESET_REASON_CLEAR) == 0) {
|
||||||
|
hal_reset_reason_clear();
|
||||||
|
greentea_send_kv(MSG_KEY_RESET_REASON, MSG_VALUE_RESET_REASON_CLEAR_ACK);
|
||||||
|
return CMD_STATUS_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(key, MSG_KEY_DEVICE_RESET) == 0 && strcmp(value, MSG_VALUE_DEVICE_RESET_NVIC) == 0) {
|
||||||
|
greentea_send_kv(MSG_KEY_DEVICE_RESET, MSG_VALUE_DEVICE_RESET_ACK);
|
||||||
|
wait_ms(SERIAL_FLUSH_TIME_MS); // Wait for the serial buffers to flush.
|
||||||
|
NVIC_SystemReset();
|
||||||
|
TEST_ASSERT_MESSAGE(0, "NVIC_SystemReset did not reset the device as expected.");
|
||||||
|
return CMD_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEVICE_WATCHDOG
|
||||||
|
if (strcmp(key, MSG_KEY_DEVICE_RESET) == 0 && strcmp(value, MSG_VALUE_DEVICE_RESET_WATCHDOG) == 0) {
|
||||||
|
greentea_send_kv(MSG_KEY_DEVICE_RESET, MSG_VALUE_DEVICE_RESET_ACK);
|
||||||
|
wait_ms(SERIAL_FLUSH_TIME_MS); // Wait for the serial buffers to flush.
|
||||||
|
watchdog_config_t config = { .timeout_ms = WDG_TIMEOUT_MS };
|
||||||
|
if (hal_watchdog_init(&config) != WATCHDOG_STATUS_OK) {
|
||||||
|
TEST_ASSERT_MESSAGE(0, "hal_watchdog_init() error.");
|
||||||
|
return CMD_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
wait_ms(2 * WDG_TIMEOUT_MS); // Watchdog should fire before twice the timeout value.
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected.");
|
||||||
|
return CMD_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Invalid message key.");
|
||||||
|
return CMD_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_reset_reason()
|
||||||
|
{
|
||||||
|
// Report readiness and watchdog status.
|
||||||
|
greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_WATCHDOG_STATUS);
|
||||||
|
|
||||||
|
cmd_status_t cmd_status = CMD_STATUS_CONTINUE;
|
||||||
|
static char _key[MSG_KEY_LEN + 1] = { };
|
||||||
|
static char _value[MSG_VALUE_LEN + 1] = { };
|
||||||
|
|
||||||
|
// Let the host side decide what to do and just handle the commands.
|
||||||
|
while (CMD_STATUS_CONTINUE == cmd_status) {
|
||||||
|
memset(_key, 0, sizeof _key);
|
||||||
|
memset(_value, 0, sizeof _value);
|
||||||
|
greentea_parse_kv(_key, _value, MSG_KEY_LEN, MSG_VALUE_LEN);
|
||||||
|
cmd_status = handle_command(_key, _value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
GREENTEA_SETUP(60, "reset_reason");
|
||||||
|
test_reset_reason(); // The result of this test suite is reported by the host side.
|
||||||
|
GREENTEA_TESTSUITE_RESULT(0); // Fail on any error.
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hal_reset_reason_tests
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MBED_HAL_RESET_REASON_API_TESTS_H
|
||||||
|
#define MBED_HAL_RESET_REASON_API_TESTS_H
|
||||||
|
|
||||||
|
#if DEVICE_RESET_REASON
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Test the Reset Reason HAL API
|
||||||
|
*
|
||||||
|
* Given a device supporting a Reset Reason API,
|
||||||
|
* when the device is restarted,
|
||||||
|
* then the device returns a correct reset reason for every restart.
|
||||||
|
*/
|
||||||
|
void test_reset_reason();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @}*/
|
||||||
|
|
|
@ -122,8 +122,27 @@ void deepsleep_lpticker_test()
|
||||||
|
|
||||||
lp_ticker_set_interrupt(next_match_timestamp);
|
lp_ticker_set_interrupt(next_match_timestamp);
|
||||||
|
|
||||||
|
/* On some targets like STM family boards with LPTIM enabled there is a required delay (~100 us) before we are able to
|
||||||
|
reprogram LPTIM_COMPARE register back to back. This is handled by the low level lp ticker wrapper which uses LPTIM_CMPOK interrupt.
|
||||||
|
CMPOK fires when LPTIM_COMPARE register can be safely reprogrammed again. During this period deep-sleep is locked.
|
||||||
|
This means that on these platforms we have additional interrupt (CMPOK) fired always ~100 us after programming lp ticker.
|
||||||
|
Since this interrupt wakes-up the board from the sleep we need to go to sleep after CMPOK is handled. */
|
||||||
|
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
|
||||||
|
|
||||||
sleep();
|
sleep();
|
||||||
|
|
||||||
|
/* On some targets like STM family boards with LPTIM enabled an interrupt is triggered on counter rollover.
|
||||||
|
We need special handling for cases when next_match_timestamp < start_timestamp (interrupt is to be fired after rollover).
|
||||||
|
In such case after first wake-up we need to reset interrupt and go back to sleep waiting for the valid one.
|
||||||
|
NOTE: Above comment (CMPOK) applies also here.*/
|
||||||
|
#if MBED_CONF_TARGET_LPTICKER_LPTIM
|
||||||
|
if ((next_match_timestamp < start_timestamp) && lp_ticker_read() < next_match_timestamp) {
|
||||||
|
lp_ticker_set_interrupt(next_match_timestamp);
|
||||||
|
wait_ns(200000);
|
||||||
|
sleep();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
const timestamp_t wakeup_timestamp = lp_ticker_read();
|
const timestamp_t wakeup_timestamp = lp_ticker_read();
|
||||||
|
|
||||||
sprintf(info, "Delta ticks: %u, Ticker width: %u, Expected wake up tick: %d, Actual wake up tick: %d, delay ticks: %d, wake up after ticks: %d",
|
sprintf(info, "Delta ticks: %u, Ticker width: %u, Expected wake up tick: %d, Actual wake up tick: %d, delay ticks: %d, wake up after ticks: %d",
|
||||||
|
@ -154,11 +173,14 @@ void deepsleep_high_speed_clocks_turned_off_test()
|
||||||
|
|
||||||
TEST_ASSERT_TRUE_MESSAGE(sleep_manager_can_deep_sleep(), "deep sleep should not be locked");
|
TEST_ASSERT_TRUE_MESSAGE(sleep_manager_can_deep_sleep(), "deep sleep should not be locked");
|
||||||
|
|
||||||
const unsigned int us_ticks_before_sleep = us_ticker_read();
|
|
||||||
|
|
||||||
const timestamp_t wakeup_time = lp_ticker_read() + us_to_ticks(20000, lp_ticker_freq);
|
const timestamp_t wakeup_time = lp_ticker_read() + us_to_ticks(20000, lp_ticker_freq);
|
||||||
lp_ticker_set_interrupt(wakeup_time);
|
lp_ticker_set_interrupt(wakeup_time);
|
||||||
|
|
||||||
|
/* Wait for CMPOK */
|
||||||
|
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
|
||||||
|
|
||||||
|
const unsigned int us_ticks_before_sleep = us_ticker_read();
|
||||||
|
|
||||||
sleep();
|
sleep();
|
||||||
|
|
||||||
const unsigned int us_ticks_after_sleep = us_ticker_read();
|
const unsigned int us_ticks_after_sleep = us_ticker_read();
|
||||||
|
|
|
@ -28,12 +28,34 @@
|
||||||
|
|
||||||
#define SLEEP_DURATION_US 20000ULL
|
#define SLEEP_DURATION_US 20000ULL
|
||||||
#define DEEP_SLEEP_TEST_CHECK_WAIT_US 2000
|
#define DEEP_SLEEP_TEST_CHECK_WAIT_US 2000
|
||||||
#define DEEP_SLEEP_TEST_CHECK_WAIT_DELTA_US 500
|
// As sleep_manager_can_deep_sleep_test_check() is based on wait_ns
|
||||||
|
// and wait_ns can be up to 40% slower, use a 50% delta here.
|
||||||
|
#define DEEP_SLEEP_TEST_CHECK_WAIT_DELTA_US 1000
|
||||||
|
|
||||||
using utest::v1::Case;
|
using utest::v1::Case;
|
||||||
using utest::v1::Specification;
|
using utest::v1::Specification;
|
||||||
using utest::v1::Harness;
|
using utest::v1::Harness;
|
||||||
|
|
||||||
|
#if DEVICE_LPTICKER
|
||||||
|
/* Make sure there are enough ticks to cope with more than SLEEP_DURATION_US sleep
|
||||||
|
* without hitting the wrap-around.
|
||||||
|
*/
|
||||||
|
void wraparound_lp_protect(void)
|
||||||
|
{
|
||||||
|
const uint32_t ticks_now = get_lp_ticker_data()->interface->read();
|
||||||
|
const ticker_info_t *p_ticker_info = get_lp_ticker_data()->interface->get_info();
|
||||||
|
|
||||||
|
const uint32_t max_count = ((1 << p_ticker_info->bits) - 1);
|
||||||
|
const uint32_t delta_ticks = us_to_ticks(SLEEP_DURATION_US * 1.5, p_ticker_info->frequency);
|
||||||
|
|
||||||
|
if (ticks_now < (max_count - delta_ticks)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (get_lp_ticker_data()->interface->read() > (max_count - delta_ticks));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void test_lock_unlock()
|
void test_lock_unlock()
|
||||||
{
|
{
|
||||||
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
||||||
|
@ -62,8 +84,6 @@ void test_lock_eq_ushrt_max()
|
||||||
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEVICE_LPTICKER
|
|
||||||
#if DEVICE_USTICKER
|
|
||||||
utest::v1::status_t testcase_setup(const Case *const source, const size_t index_of_case)
|
utest::v1::status_t testcase_setup(const Case *const source, const size_t index_of_case)
|
||||||
{
|
{
|
||||||
// Suspend the RTOS kernel scheduler to prevent interference with duration of sleep.
|
// Suspend the RTOS kernel scheduler to prevent interference with duration of sleep.
|
||||||
|
@ -98,6 +118,8 @@ utest::v1::status_t testcase_teardown(const Case *const source, const size_t pas
|
||||||
return utest::v1::greentea_case_teardown_handler(source, passed, failed, failure);
|
return utest::v1::greentea_case_teardown_handler(source, passed, failed, failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEVICE_LPTICKER
|
||||||
|
#if DEVICE_USTICKER
|
||||||
/* This test is based on the fact that the high-speed clocks are turned off
|
/* This test is based on the fact that the high-speed clocks are turned off
|
||||||
* in deep sleep mode but remain on in the ordinary sleep mode. Low-speed
|
* in deep sleep mode but remain on in the ordinary sleep mode. Low-speed
|
||||||
* clocks stay on for both sleep and deep sleep modes.
|
* clocks stay on for both sleep and deep sleep modes.
|
||||||
|
@ -115,10 +137,18 @@ void test_sleep_auto()
|
||||||
const ticker_irq_handler_type lp_ticker_irq_handler_org = set_lp_ticker_irq_handler(lp_ticker_isr);
|
const ticker_irq_handler_type lp_ticker_irq_handler_org = set_lp_ticker_irq_handler(lp_ticker_isr);
|
||||||
us_timestamp_t us_ts1, us_ts2, lp_ts1, lp_ts2, us_diff1, us_diff2, lp_diff1, lp_diff2;
|
us_timestamp_t us_ts1, us_ts2, lp_ts1, lp_ts2, us_diff1, us_diff2, lp_diff1, lp_diff2;
|
||||||
|
|
||||||
sleep_manager_lock_deep_sleep();
|
/* Let's avoid the Lp ticker wrap-around case */
|
||||||
|
wraparound_lp_protect();
|
||||||
uint32_t lp_wakeup_ts_raw = lp_ticker_read() + us_to_ticks(SLEEP_DURATION_US, lp_ticker_info->frequency);
|
uint32_t lp_wakeup_ts_raw = lp_ticker_read() + us_to_ticks(SLEEP_DURATION_US, lp_ticker_info->frequency);
|
||||||
timestamp_t lp_wakeup_ts = overflow_protect(lp_wakeup_ts_raw, lp_ticker_info->bits);
|
timestamp_t lp_wakeup_ts = overflow_protect(lp_wakeup_ts_raw, lp_ticker_info->bits);
|
||||||
lp_ticker_set_interrupt(lp_wakeup_ts);
|
lp_ticker_set_interrupt(lp_wakeup_ts);
|
||||||
|
|
||||||
|
/* Some targets may need an interrupt short time after LPTIM interrupt is
|
||||||
|
* set and forbid deep_sleep during that period. Let this period pass */
|
||||||
|
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
|
||||||
|
|
||||||
|
sleep_manager_lock_deep_sleep();
|
||||||
|
|
||||||
us_ts1 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
|
us_ts1 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
|
||||||
lp_ts1 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);
|
lp_ts1 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);
|
||||||
|
|
||||||
|
@ -144,13 +174,21 @@ void test_sleep_auto()
|
||||||
// Wait for hardware serial buffers to flush.
|
// Wait for hardware serial buffers to flush.
|
||||||
busy_wait_ms(SERIAL_FLUSH_TIME_MS);
|
busy_wait_ms(SERIAL_FLUSH_TIME_MS);
|
||||||
|
|
||||||
|
/* Let's avoid the Lp ticker wrap-around case */
|
||||||
|
wraparound_lp_protect();
|
||||||
lp_wakeup_ts_raw = lp_ticker_read() + us_to_ticks(SLEEP_DURATION_US, lp_ticker_info->frequency);
|
lp_wakeup_ts_raw = lp_ticker_read() + us_to_ticks(SLEEP_DURATION_US, lp_ticker_info->frequency);
|
||||||
lp_wakeup_ts = overflow_protect(lp_wakeup_ts_raw, lp_ticker_info->bits);
|
lp_wakeup_ts = overflow_protect(lp_wakeup_ts_raw, lp_ticker_info->bits);
|
||||||
lp_ticker_set_interrupt(lp_wakeup_ts);
|
lp_ticker_set_interrupt(lp_wakeup_ts);
|
||||||
|
|
||||||
|
/* Some targets may need an interrupt short time after LPTIM interrupt is
|
||||||
|
* set and forbid deep_sleep during that period. Let this period pass */
|
||||||
|
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
|
||||||
|
|
||||||
us_ts1 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
|
us_ts1 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
|
||||||
lp_ts1 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);
|
lp_ts1 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);
|
||||||
|
|
||||||
sleep_manager_sleep_auto();
|
sleep_manager_sleep_auto();
|
||||||
|
|
||||||
us_ts2 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
|
us_ts2 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
|
||||||
us_diff2 = (us_ts1 <= us_ts2) ? (us_ts2 - us_ts1) : (us_ticker_mask - us_ts1 + us_ts2 + 1);
|
us_diff2 = (us_ts1 <= us_ts2) ? (us_ts2 - us_ts1) : (us_ticker_mask - us_ts1 + us_ts2 + 1);
|
||||||
lp_ts2 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);
|
lp_ts2 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);
|
||||||
|
@ -175,65 +213,106 @@ void test_sleep_auto()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define US_PER_S 1000000
|
||||||
|
|
||||||
|
uint32_t diff_us(uint32_t start_ticks, uint32_t stop_ticks, const ticker_info_t *info)
|
||||||
|
{
|
||||||
|
uint32_t counter_mask = ((1 << info->bits) - 1);
|
||||||
|
|
||||||
|
uint32_t diff_ticks = ((stop_ticks - start_ticks) & counter_mask);
|
||||||
|
|
||||||
|
return (uint32_t)((uint64_t) diff_ticks * US_PER_S / info->frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
volatile bool unlock_deep_sleep = false;
|
||||||
|
|
||||||
|
void ticker_event_handler_stub(const ticker_data_t *const ticker)
|
||||||
|
{
|
||||||
|
lp_ticker_clear_interrupt();
|
||||||
|
if (unlock_deep_sleep) {
|
||||||
|
sleep_manager_unlock_deep_sleep_internal();
|
||||||
|
unlock_deep_sleep = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker_irq_handler_type prev_irq_handler;
|
||||||
|
|
||||||
void test_lock_unlock_test_check()
|
void test_lock_unlock_test_check()
|
||||||
{
|
{
|
||||||
// Make sure HAL tickers are initialized.
|
prev_irq_handler = set_lp_ticker_irq_handler(ticker_event_handler_stub);
|
||||||
ticker_read(get_us_ticker_data());
|
|
||||||
ticker_read(get_lp_ticker_data());
|
|
||||||
|
|
||||||
// Use LowPowerTimer instead of Timer to prevent deep sleep lock.
|
for (int i = 0; i < 1000; i++) {
|
||||||
LowPowerTimer lp_timer;
|
|
||||||
us_timestamp_t exec_time_unlocked, exec_time_locked;
|
|
||||||
LowPowerTimeout lp_timeout;
|
|
||||||
|
|
||||||
// Deep sleep unlocked:
|
wraparound_lp_protect();
|
||||||
// * sleep_manager_can_deep_sleep() returns true,
|
|
||||||
// * sleep_manager_can_deep_sleep_test_check() returns true instantly.
|
|
||||||
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
|
||||||
lp_timer.start();
|
|
||||||
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
|
|
||||||
lp_timer.stop();
|
|
||||||
exec_time_unlocked = lp_timer.read_high_resolution_us();
|
|
||||||
|
|
||||||
// Deep sleep locked:
|
const ticker_info_t *p_ticker_info = get_lp_ticker_data()->interface->get_info();
|
||||||
// * sleep_manager_can_deep_sleep() returns false,
|
|
||||||
// * sleep_manager_can_deep_sleep_test_check() returns false with 2 ms delay.
|
|
||||||
sleep_manager_lock_deep_sleep();
|
|
||||||
TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
|
|
||||||
lp_timer.reset();
|
|
||||||
lp_timer.start();
|
|
||||||
TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep_test_check());
|
|
||||||
lp_timer.stop();
|
|
||||||
exec_time_locked = lp_timer.read_high_resolution_us();
|
|
||||||
TEST_ASSERT_UINT64_WITHIN(DEEP_SLEEP_TEST_CHECK_WAIT_DELTA_US, DEEP_SLEEP_TEST_CHECK_WAIT_US,
|
|
||||||
exec_time_locked - exec_time_unlocked);
|
|
||||||
|
|
||||||
// Deep sleep unlocked with a 1 ms delay:
|
// Use LowPowerTimer instead of Timer to prevent deep sleep lock.
|
||||||
// * sleep_manager_can_deep_sleep() returns false,
|
us_timestamp_t exec_time_unlocked, exec_time_locked;
|
||||||
// * sleep_manager_can_deep_sleep_test_check() returns true with a 1 ms delay,
|
uint32_t start, stop;
|
||||||
// * sleep_manager_can_deep_sleep() returns true when checked again.
|
|
||||||
lp_timer.reset();
|
// Deep sleep unlocked:
|
||||||
lp_timeout.attach_us(mbed::callback(sleep_manager_unlock_deep_sleep_internal),
|
// * sleep_manager_can_deep_sleep() returns true,
|
||||||
DEEP_SLEEP_TEST_CHECK_WAIT_US / 2);
|
// * sleep_manager_can_deep_sleep_test_check() returns true instantly.
|
||||||
lp_timer.start();
|
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
||||||
TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
|
start = lp_ticker_read();
|
||||||
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
|
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
|
||||||
lp_timer.stop();
|
stop = lp_ticker_read();
|
||||||
TEST_ASSERT_UINT64_WITHIN(DEEP_SLEEP_TEST_CHECK_WAIT_DELTA_US, DEEP_SLEEP_TEST_CHECK_WAIT_US / 2,
|
exec_time_unlocked = diff_us(start, stop, p_ticker_info);
|
||||||
lp_timer.read_high_resolution_us());
|
|
||||||
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
// Deep sleep locked:
|
||||||
|
// * sleep_manager_can_deep_sleep() returns false,
|
||||||
|
// * sleep_manager_can_deep_sleep_test_check() returns false with 2 ms delay.
|
||||||
|
sleep_manager_lock_deep_sleep();
|
||||||
|
TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
|
||||||
|
start = lp_ticker_read();
|
||||||
|
TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep_test_check());
|
||||||
|
stop = lp_ticker_read();
|
||||||
|
exec_time_locked = diff_us(start, stop, p_ticker_info);
|
||||||
|
TEST_ASSERT_UINT64_WITHIN(DEEP_SLEEP_TEST_CHECK_WAIT_DELTA_US, DEEP_SLEEP_TEST_CHECK_WAIT_US,
|
||||||
|
exec_time_locked - exec_time_unlocked);
|
||||||
|
|
||||||
|
// Deep sleep unlocked with a 1 ms delay:
|
||||||
|
// * sleep_manager_can_deep_sleep() returns false,
|
||||||
|
// * sleep_manager_can_deep_sleep_test_check() returns true with a 1 ms delay,
|
||||||
|
// * sleep_manager_can_deep_sleep() returns true when checked again.
|
||||||
|
unlock_deep_sleep = true;
|
||||||
|
/* Let's avoid the Lp ticker wrap-around case */
|
||||||
|
wraparound_lp_protect();
|
||||||
|
start = lp_ticker_read();
|
||||||
|
uint32_t lp_wakeup_ts_raw = start + us_to_ticks(DEEP_SLEEP_TEST_CHECK_WAIT_US / 2, p_ticker_info->frequency);
|
||||||
|
timestamp_t lp_wakeup_ts = overflow_protect(lp_wakeup_ts_raw, p_ticker_info->bits);
|
||||||
|
lp_ticker_set_interrupt(lp_wakeup_ts);
|
||||||
|
|
||||||
|
// Extra wait after setting interrupt to handle CMPOK
|
||||||
|
wait_ns(100000);
|
||||||
|
TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
|
||||||
|
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
|
||||||
|
stop = lp_ticker_read();
|
||||||
|
TEST_ASSERT(diff_us(start, stop, p_ticker_info) > 0UL);
|
||||||
|
TEST_ASSERT(diff_us(start, stop, p_ticker_info) < DEEP_SLEEP_TEST_CHECK_WAIT_US);
|
||||||
|
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
||||||
|
}
|
||||||
|
|
||||||
|
set_lp_ticker_irq_handler(prev_irq_handler);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
utest::v1::status_t testsuite_setup(const size_t number_of_cases)
|
utest::v1::status_t testsuite_setup(const size_t number_of_cases)
|
||||||
{
|
{
|
||||||
GREENTEA_SETUP(10, "default_auto");
|
GREENTEA_SETUP(15, "default_auto");
|
||||||
return utest::v1::greentea_test_setup_handler(number_of_cases);
|
return utest::v1::greentea_test_setup_handler(number_of_cases);
|
||||||
}
|
}
|
||||||
|
|
||||||
Case cases[] = {
|
Case cases[] = {
|
||||||
Case("deep sleep lock/unlock", test_lock_unlock),
|
Case("deep sleep lock/unlock",
|
||||||
Case("deep sleep locked USHRT_MAX times", test_lock_eq_ushrt_max),
|
(utest::v1::case_setup_handler_t) testcase_setup,
|
||||||
|
test_lock_unlock,
|
||||||
|
(utest::v1::case_teardown_handler_t) testcase_teardown),
|
||||||
|
Case("deep sleep locked USHRT_MAX times",
|
||||||
|
(utest::v1::case_setup_handler_t) testcase_setup,
|
||||||
|
test_lock_eq_ushrt_max,
|
||||||
|
(utest::v1::case_teardown_handler_t) testcase_teardown),
|
||||||
#if DEVICE_LPTICKER
|
#if DEVICE_LPTICKER
|
||||||
#if DEVICE_USTICKER
|
#if DEVICE_USTICKER
|
||||||
Case("sleep_auto calls sleep/deep sleep based on lock",
|
Case("sleep_auto calls sleep/deep sleep based on lock",
|
||||||
|
@ -241,7 +320,10 @@ Case cases[] = {
|
||||||
test_sleep_auto,
|
test_sleep_auto,
|
||||||
(utest::v1::case_teardown_handler_t) testcase_teardown),
|
(utest::v1::case_teardown_handler_t) testcase_teardown),
|
||||||
#endif
|
#endif
|
||||||
Case("deep sleep lock/unlock test_check", test_lock_unlock_test_check),
|
Case("deep sleep lock/unlock test_check",
|
||||||
|
(utest::v1::case_setup_handler_t) testcase_setup,
|
||||||
|
test_lock_unlock_test_check,
|
||||||
|
(utest::v1::case_teardown_handler_t) testcase_teardown)
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,8 @@
|
||||||
#error [NOT_SUPPORTED] this test is supported on GCC only
|
#error [NOT_SUPPORTED] this test is supported on GCC only
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__CORTEX_M33)
|
#if DOMAIN_NS == 1
|
||||||
#error [NOT_SUPPORTED] Cannot run on M33 core as SecureFault is implemented in secure-side and cant be remapped
|
#error [NOT_SUPPORTED] Cannot run on M23/M33 core as SecureFault is implemented in secure-side and cant be remapped
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "utest/utest.h"
|
#include "utest/utest.h"
|
||||||
|
|
|
@ -34,6 +34,11 @@ void us_ticker_info_test()
|
||||||
TEST_ASSERT(p_ticker_info->frequency >= 250000);
|
TEST_ASSERT(p_ticker_info->frequency >= 250000);
|
||||||
TEST_ASSERT(p_ticker_info->frequency <= 8000000);
|
TEST_ASSERT(p_ticker_info->frequency <= 8000000);
|
||||||
TEST_ASSERT(p_ticker_info->bits >= 16);
|
TEST_ASSERT(p_ticker_info->bits >= 16);
|
||||||
|
|
||||||
|
#ifdef US_TICKER_PERIOD_NUM
|
||||||
|
TEST_ASSERT_UINT32_WITHIN(1, 1000000 * US_TICKER_PERIOD_DEN / US_TICKER_PERIOD_NUM, p_ticker_info->frequency);
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(US_TICKER_MASK, ((uint64_t)1 << p_ticker_info->bits) - 1);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
utest::v1::status_t test_setup(const size_t number_of_cases)
|
utest::v1::status_t test_setup(const size_t number_of_cases)
|
||||||
|
|
|
@ -0,0 +1,285 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#if !DEVICE_WATCHDOG
|
||||||
|
#error [NOT_SUPPORTED] Watchdog not supported for this target
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "greentea-client/test_env.h"
|
||||||
|
#include "hal/watchdog_api.h"
|
||||||
|
#include "mbed_wait_api.h"
|
||||||
|
#include "unity/unity.h"
|
||||||
|
#include "utest/utest.h"
|
||||||
|
#include "watchdog_api_tests.h"
|
||||||
|
#include "mbed.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* The shortest timeout value, this test suite is able to handle correctly. */
|
||||||
|
#define WDG_MIN_TIMEOUT_MS 50UL
|
||||||
|
|
||||||
|
// Do not set watchdog timeout shorter than WDG_MIN_TIMEOUT_MS, as it may
|
||||||
|
// cause the host-test-runner return 'TIMEOUT' instead of 'FAIL' / 'PASS'
|
||||||
|
// if watchdog performs reset during test suite teardown.
|
||||||
|
#define WDG_TIMEOUT_MS 100UL
|
||||||
|
|
||||||
|
#define MSG_VALUE_DUMMY "0"
|
||||||
|
#define MSG_VALUE_LEN 24
|
||||||
|
#define MSG_KEY_LEN 24
|
||||||
|
|
||||||
|
#define MSG_KEY_DEVICE_READY "ready"
|
||||||
|
#define MSG_KEY_START_CASE "start_case"
|
||||||
|
#define MSG_KEY_DEVICE_RESET "reset_on_case_teardown"
|
||||||
|
|
||||||
|
/* To prevent a loss of Greentea data, the serial buffers have to be flushed
|
||||||
|
* before the UART peripheral shutdown. The UART shutdown happens when the
|
||||||
|
* device is entering the deepsleep mode or performing a reset.
|
||||||
|
*
|
||||||
|
* With the current API, it is not possible to check if the hardware buffers
|
||||||
|
* are empty. However, it is possible to determine the time required for the
|
||||||
|
* buffers to flush.
|
||||||
|
*
|
||||||
|
* Take NUMAKER_PFM_NUC472 as an example:
|
||||||
|
* The UART peripheral has 16-byte Tx FIFO. With a baud rate set to 9600,
|
||||||
|
* flushing the Tx FIFO would take: 16 * 8 * 1000 / 9600 = 13.3 ms.
|
||||||
|
* To be on the safe side, set the wait time to 20 ms.
|
||||||
|
*/
|
||||||
|
#define SERIAL_FLUSH_TIME_MS 20
|
||||||
|
|
||||||
|
int CASE_INDEX_START;
|
||||||
|
int CASE_INDEX_CURRENT;
|
||||||
|
bool CASE_IGNORED = false;
|
||||||
|
|
||||||
|
using utest::v1::Case;
|
||||||
|
using utest::v1::Specification;
|
||||||
|
using utest::v1::Harness;
|
||||||
|
|
||||||
|
const watchdog_config_t WDG_CONFIG_DEFAULT = { .timeout_ms = WDG_TIMEOUT_MS };
|
||||||
|
|
||||||
|
Thread wdg_kicking_thread;
|
||||||
|
Semaphore kick_wdg_during_test_teardown(0, 1);
|
||||||
|
|
||||||
|
void wdg_kicking_thread_fun()
|
||||||
|
{
|
||||||
|
kick_wdg_during_test_teardown.wait();
|
||||||
|
while (true) {
|
||||||
|
hal_watchdog_kick();
|
||||||
|
wait_ms(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_max_timeout_is_valid()
|
||||||
|
{
|
||||||
|
TEST_ASSERT(hal_watchdog_get_platform_features().max_timeout > 1UL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_restart_is_possible()
|
||||||
|
{
|
||||||
|
watchdog_features_t features = hal_watchdog_get_platform_features();
|
||||||
|
if (!features.disable_watchdog) {
|
||||||
|
CASE_IGNORED = true;
|
||||||
|
TEST_IGNORE_MESSAGE("Disabling watchdog not supported for this platform");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TEST_ASSERT(features.update_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_stop()
|
||||||
|
{
|
||||||
|
watchdog_features_t features = hal_watchdog_get_platform_features();
|
||||||
|
if (!features.disable_watchdog) {
|
||||||
|
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_NOT_SUPPORTED, hal_watchdog_stop());
|
||||||
|
CASE_IGNORED = true;
|
||||||
|
TEST_IGNORE_MESSAGE("Disabling watchdog not supported for this platform");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_stop());
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&WDG_CONFIG_DEFAULT));
|
||||||
|
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_stop());
|
||||||
|
// Make sure that a disabled watchdog does not reset the core.
|
||||||
|
wait_ms(2 * WDG_TIMEOUT_MS); // Watchdog should fire before twice the timeout value.
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_stop());
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_update_config()
|
||||||
|
{
|
||||||
|
watchdog_features_t features = hal_watchdog_get_platform_features();
|
||||||
|
if (!features.update_config) {
|
||||||
|
CASE_IGNORED = true;
|
||||||
|
TEST_IGNORE_MESSAGE("Updating watchdog config not supported for this platform");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
watchdog_config_t config = WDG_CONFIG_DEFAULT;
|
||||||
|
uint32_t timeouts[] = {
|
||||||
|
features.max_timeout / 4,
|
||||||
|
features.max_timeout / 8,
|
||||||
|
features.max_timeout / 16
|
||||||
|
};
|
||||||
|
int num_timeouts = sizeof timeouts / sizeof timeouts[0];
|
||||||
|
|
||||||
|
for (size_t i = 0; i < num_timeouts; i++) {
|
||||||
|
if (timeouts[i] < WDG_MIN_TIMEOUT_MS) {
|
||||||
|
CASE_IGNORED = true;
|
||||||
|
TEST_IGNORE_MESSAGE("Requested timeout value is too short -- ignoring test case.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.timeout_ms = timeouts[i];
|
||||||
|
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config));
|
||||||
|
uint32_t reload_value = hal_watchdog_get_reload_value();
|
||||||
|
// The watchdog should trigger at, or after the timeout value.
|
||||||
|
TEST_ASSERT(reload_value >= timeouts[i]);
|
||||||
|
// The watchdog should trigger before twice the timeout value.
|
||||||
|
TEST_ASSERT(reload_value < 2 * timeouts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
utest::v1::status_t case_setup_sync_on_reset(const Case *const source, const size_t index_of_case)
|
||||||
|
{
|
||||||
|
CASE_INDEX_CURRENT = index_of_case;
|
||||||
|
CASE_IGNORED = false;
|
||||||
|
return utest::v1::greentea_case_setup_handler(source, index_of_case);
|
||||||
|
}
|
||||||
|
|
||||||
|
utest::v1::status_t case_teardown_sync_on_reset(const Case *const source, const size_t passed, const size_t failed,
|
||||||
|
const utest::v1::failure_t failure)
|
||||||
|
{
|
||||||
|
if (CASE_IGNORED) {
|
||||||
|
return utest::v1::greentea_case_teardown_handler(source, passed, failed, failure);
|
||||||
|
}
|
||||||
|
// Unlock kicking the watchdog during teardown.
|
||||||
|
kick_wdg_during_test_teardown.release();
|
||||||
|
utest::v1::status_t status = utest::v1::greentea_case_teardown_handler(source, passed, failed, failure);
|
||||||
|
if (failed) {
|
||||||
|
/* Return immediately and skip the device reset, if the test case failed.
|
||||||
|
* Provided that the device won't be restarted by other means (i.e. watchdog timer),
|
||||||
|
* this should allow the test suite to finish in a defined manner
|
||||||
|
* and report failure to host.
|
||||||
|
* In case of watchdog reset during test suite teardown, the loss of serial
|
||||||
|
* connection is possible, so the host-test-runner may return 'TIMEOUT'
|
||||||
|
* instead of 'FAIL'.
|
||||||
|
*/
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
greentea_send_kv(MSG_KEY_DEVICE_RESET, CASE_INDEX_START + CASE_INDEX_CURRENT);
|
||||||
|
utest_printf("The device will now restart.\n");
|
||||||
|
wait_ms(SERIAL_FLUSH_TIME_MS); // Wait for the serial buffers to flush.
|
||||||
|
NVIC_SystemReset();
|
||||||
|
return status; // Reset is instant so this line won't be reached.
|
||||||
|
}
|
||||||
|
|
||||||
|
utest::v1::status_t case_teardown_wdg_stop_or_reset(const Case *const source, const size_t passed, const size_t failed,
|
||||||
|
const utest::v1::failure_t failure)
|
||||||
|
{
|
||||||
|
if (CASE_IGNORED) {
|
||||||
|
return utest::v1::greentea_case_teardown_handler(source, passed, failed, failure);
|
||||||
|
}
|
||||||
|
watchdog_features_t features = hal_watchdog_get_platform_features();
|
||||||
|
if (features.disable_watchdog) {
|
||||||
|
hal_watchdog_stop();
|
||||||
|
return utest::v1::greentea_case_teardown_handler(source, passed, failed, failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
return case_teardown_sync_on_reset(source, passed, failed, failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<uint32_t timeout_ms>
|
||||||
|
void test_init()
|
||||||
|
{
|
||||||
|
if (timeout_ms < WDG_MIN_TIMEOUT_MS) {
|
||||||
|
CASE_IGNORED = true;
|
||||||
|
TEST_IGNORE_MESSAGE("Requested timeout value is too short -- ignoring test case.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
watchdog_config_t config = { .timeout_ms = timeout_ms };
|
||||||
|
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config));
|
||||||
|
uint32_t reload_value = hal_watchdog_get_reload_value();
|
||||||
|
// The watchdog should trigger at, or after the timeout value.
|
||||||
|
TEST_ASSERT(reload_value >= timeout_ms);
|
||||||
|
// The watchdog should trigger before twice the timeout value.
|
||||||
|
TEST_ASSERT(reload_value < 2 * timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_init_max_timeout()
|
||||||
|
{
|
||||||
|
watchdog_features_t features = hal_watchdog_get_platform_features();
|
||||||
|
watchdog_config_t config = { .timeout_ms = features.max_timeout };
|
||||||
|
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config));
|
||||||
|
// The watchdog should trigger at, or after the timeout value.
|
||||||
|
TEST_ASSERT(hal_watchdog_get_reload_value() >= features.max_timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
int testsuite_setup_sync_on_reset(const size_t number_of_cases)
|
||||||
|
{
|
||||||
|
GREENTEA_SETUP(45, "sync_on_reset");
|
||||||
|
utest::v1::status_t status = utest::v1::greentea_test_setup_handler(number_of_cases);
|
||||||
|
if (status != utest::v1::STATUS_CONTINUE) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
char key[MSG_KEY_LEN + 1] = { };
|
||||||
|
char value[MSG_VALUE_LEN + 1] = { };
|
||||||
|
|
||||||
|
greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_DUMMY);
|
||||||
|
greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN);
|
||||||
|
|
||||||
|
if (strcmp(key, MSG_KEY_START_CASE) != 0) {
|
||||||
|
utest_printf("Invalid message key.\n");
|
||||||
|
return utest::v1::STATUS_ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *tailptr = NULL;
|
||||||
|
CASE_INDEX_START = (int) strtol(value, &tailptr, 10);
|
||||||
|
if (*tailptr != '\0' || CASE_INDEX_START < 0) {
|
||||||
|
utest_printf("Invalid start case index received from host\n");
|
||||||
|
return utest::v1::STATUS_ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The thread is started here, but feeding the watchdog will start
|
||||||
|
// when the semaphore is released during a test case teardown.
|
||||||
|
wdg_kicking_thread.start(mbed::callback(wdg_kicking_thread_fun));
|
||||||
|
|
||||||
|
utest_printf("Starting with test case index %i of all %i defined test cases.\n", CASE_INDEX_START, number_of_cases);
|
||||||
|
return CASE_INDEX_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
Case cases[] = {
|
||||||
|
Case("Platform feature max_timeout is valid", test_max_timeout_is_valid),
|
||||||
|
Case("Stopped watchdog can be started again", test_restart_is_possible),
|
||||||
|
Case("Watchdog can be stopped", test_stop),
|
||||||
|
|
||||||
|
Case("Update config with multiple init calls",
|
||||||
|
(utest::v1::case_setup_handler_t) case_setup_sync_on_reset,
|
||||||
|
test_update_config,
|
||||||
|
(utest::v1::case_teardown_handler_t) case_teardown_wdg_stop_or_reset),
|
||||||
|
|
||||||
|
Case("Init, 100 ms", (utest::v1::case_setup_handler_t) case_setup_sync_on_reset,
|
||||||
|
test_init<100UL>, (utest::v1::case_teardown_handler_t) case_teardown_sync_on_reset),
|
||||||
|
Case("Init, max_timeout", (utest::v1::case_setup_handler_t) case_setup_sync_on_reset,
|
||||||
|
test_init_max_timeout, (utest::v1::case_teardown_handler_t) case_teardown_sync_on_reset),
|
||||||
|
};
|
||||||
|
|
||||||
|
Specification specification((utest::v1::test_setup_handler_t) testsuite_setup_sync_on_reset, cases);
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Harness will start with a test case index provided by host script.
|
||||||
|
return !Harness::run(specification);
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hal_watchdog_tests
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MBED_HAL_WATCHDOG_API_TESTS_H
|
||||||
|
#define MBED_HAL_WATCHDOG_API_TESTS_H
|
||||||
|
|
||||||
|
#if DEVICE_WATCHDOG
|
||||||
|
|
||||||
|
/** Test max_timeout validity
|
||||||
|
*
|
||||||
|
* Given a device supporting Watchdog HAL API,
|
||||||
|
* when @a hal_watchdog_get_platform_features() is called,
|
||||||
|
* then max_timeout member of returned watchdog_features_t struct is greater than 1.
|
||||||
|
*/
|
||||||
|
void test_max_timeout_is_valid();
|
||||||
|
|
||||||
|
/** Test Watchdog features if a stopped Watchdog can be started again
|
||||||
|
*
|
||||||
|
* Given a device supporting Watchdog HAL API,
|
||||||
|
* when the device supports the @a disable_watchdog feature,
|
||||||
|
* then the device also supports @a update_config feature.
|
||||||
|
*/
|
||||||
|
void test_restart_is_possible();
|
||||||
|
|
||||||
|
/** Test Watchdog stop
|
||||||
|
*
|
||||||
|
* Given a device without a support for the @a disable_watchdog feature,
|
||||||
|
* when @a hal_watchdog_stop() is called,
|
||||||
|
* then WATCHDOG_STATUS_NOT_SUPPORTED is returned.
|
||||||
|
*
|
||||||
|
* Otherwise, given the device with @a disable_watchdog feature support:
|
||||||
|
*
|
||||||
|
* Given the Watchdog is *NOT* running,
|
||||||
|
* when @a hal_watchdog_stop() is called,
|
||||||
|
* then WATCHDOG_STATUS_OK is returned.
|
||||||
|
*
|
||||||
|
* Given the Watchdog is running,
|
||||||
|
* when @a hal_watchdog_stop() is called before the timeout expires,
|
||||||
|
* then WATCHDOG_STATUS_OK is returned and the device is not restarted.
|
||||||
|
*
|
||||||
|
* Given the Watchdog is *NOT* running (it has already been stopped),
|
||||||
|
* when @a hal_watchdog_stop() is called,
|
||||||
|
* then WATCHDOG_STATUS_OK is returned.
|
||||||
|
*/
|
||||||
|
void test_stop();
|
||||||
|
|
||||||
|
/** Test Watchdog init multiple times
|
||||||
|
*
|
||||||
|
* Given a set of unique timeout values,
|
||||||
|
* when @a config.timeout_ms is set to each of these values (T),
|
||||||
|
* then, for every value T, @a hal_watchdog_init() returns @a WATCHDOG_STATUS_OK
|
||||||
|
* and @a hal_watchdog_get_reload_value() returns a reload value R
|
||||||
|
* and T <= R < 2 * T.
|
||||||
|
*/
|
||||||
|
void test_update_config();
|
||||||
|
|
||||||
|
/** Test Watchdog init with a valid config
|
||||||
|
*
|
||||||
|
* Given @a config.timeout_ms is set to T ms,
|
||||||
|
* which is within supported Watchdog timeout range,
|
||||||
|
* when @a hal_watchdog_init() is called,
|
||||||
|
* then @a WATCHDOG_STATUS_OK is returned
|
||||||
|
* and @a hal_watchdog_get_reload_value() returns a reload value R
|
||||||
|
* and T <= R < 2 * T.
|
||||||
|
*/
|
||||||
|
template<uint32_t timeout_ms>
|
||||||
|
void test_init();
|
||||||
|
|
||||||
|
/** Test Watchdog init with a max_timeout
|
||||||
|
*
|
||||||
|
* Given @a config.timeout_ms is set to max_timeout,
|
||||||
|
* which is a value returned by @a hal_watchdog_get_platform_features(),
|
||||||
|
* when @a hal_watchdog_init() is called,
|
||||||
|
* then @a WATCHDOG_STATUS_OK is returned
|
||||||
|
* and @a hal_watchdog_get_reload_value() returns max_timeout.
|
||||||
|
*/
|
||||||
|
void test_init_max_timeout();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @}*/
|
||||||
|
|
|
@ -0,0 +1,307 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#if !DEVICE_WATCHDOG
|
||||||
|
#error [NOT_SUPPORTED] Watchdog not supported for this target
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "greentea-client/test_env.h"
|
||||||
|
#include "utest/utest.h"
|
||||||
|
#include "unity/unity.h"
|
||||||
|
#include "hal/watchdog_api.h"
|
||||||
|
#include "watchdog_reset_tests.h"
|
||||||
|
#include "mbed.h"
|
||||||
|
|
||||||
|
#define TIMEOUT_MS 100UL
|
||||||
|
#define KICK_ADVANCE_MS 10UL
|
||||||
|
|
||||||
|
#define MSG_VALUE_DUMMY "0"
|
||||||
|
#define CASE_DATA_INVALID 0xffffffffUL
|
||||||
|
#define CASE_DATA_PHASE2_OK 0xfffffffeUL
|
||||||
|
|
||||||
|
#define MSG_VALUE_LEN 24
|
||||||
|
#define MSG_KEY_LEN 24
|
||||||
|
|
||||||
|
#define MSG_KEY_DEVICE_READY "ready"
|
||||||
|
#define MSG_KEY_START_CASE "start_case"
|
||||||
|
#define MSG_KEY_DEVICE_RESET "dev_reset"
|
||||||
|
|
||||||
|
/* To prevent a loss of Greentea data, the serial buffers have to be flushed
|
||||||
|
* before the UART peripheral shutdown. The UART shutdown happens when the
|
||||||
|
* device is entering the deepsleep mode or performing a reset.
|
||||||
|
*
|
||||||
|
* With the current API, it is not possible to check if the hardware buffers
|
||||||
|
* are empty. However, it is possible to determine the time required for the
|
||||||
|
* buffers to flush.
|
||||||
|
*
|
||||||
|
* Take NUMAKER_PFM_NUC472 as an example:
|
||||||
|
* The UART peripheral has 16-byte Tx FIFO. With a baud rate set to 9600,
|
||||||
|
* flushing the Tx FIFO would take: 16 * 8 * 1000 / 9600 = 13.3 ms.
|
||||||
|
* To be on the safe side, set the wait time to 20 ms.
|
||||||
|
*/
|
||||||
|
#define SERIAL_FLUSH_TIME_MS 20
|
||||||
|
|
||||||
|
using utest::v1::Case;
|
||||||
|
using utest::v1::Specification;
|
||||||
|
using utest::v1::Harness;
|
||||||
|
|
||||||
|
struct testcase_data {
|
||||||
|
int index;
|
||||||
|
int start_index;
|
||||||
|
uint32_t received_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
void release_sem(Semaphore *sem)
|
||||||
|
{
|
||||||
|
sem->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
testcase_data current_case;
|
||||||
|
|
||||||
|
bool send_reset_notification(testcase_data *tcdata, uint32_t delay_ms)
|
||||||
|
{
|
||||||
|
char msg_value[12];
|
||||||
|
int str_len = snprintf(msg_value, sizeof msg_value, "%02x,%08lx", tcdata->start_index + tcdata->index, delay_ms);
|
||||||
|
if (str_len != (sizeof msg_value) - 1) {
|
||||||
|
utest_printf("Failed to compose a value string to be sent to host.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
greentea_send_kv(MSG_KEY_DEVICE_RESET, msg_value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_simple_reset()
|
||||||
|
{
|
||||||
|
// Phase 2. -- verify the test results.
|
||||||
|
// Verify if this test case passed based on data received from host.
|
||||||
|
if (current_case.received_data != CASE_DATA_INVALID) {
|
||||||
|
TEST_ASSERT_EQUAL(CASE_DATA_PHASE2_OK, current_case.received_data);
|
||||||
|
current_case.received_data = CASE_DATA_INVALID;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 1. -- run the test code.
|
||||||
|
// Init the watchdog and wait for a device reset.
|
||||||
|
watchdog_config_t config = { TIMEOUT_MS };
|
||||||
|
if (send_reset_notification(¤t_case, 2 * TIMEOUT_MS) == false) {
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Dev-host communication error.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config));
|
||||||
|
// Watchdog should fire before twice the timeout value.
|
||||||
|
wait_ms(2 * TIMEOUT_MS); // Device reset expected.
|
||||||
|
|
||||||
|
// Watchdog reset should have occurred during wait_ms() above;
|
||||||
|
|
||||||
|
hal_watchdog_kick(); // Just to buy some time for testsuite failure handling.
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEVICE_SLEEP
|
||||||
|
void test_sleep_reset()
|
||||||
|
{
|
||||||
|
// Phase 2. -- verify the test results.
|
||||||
|
if (current_case.received_data != CASE_DATA_INVALID) {
|
||||||
|
TEST_ASSERT_EQUAL(CASE_DATA_PHASE2_OK, current_case.received_data);
|
||||||
|
current_case.received_data = CASE_DATA_INVALID;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 1. -- run the test code.
|
||||||
|
watchdog_config_t config = { TIMEOUT_MS };
|
||||||
|
Semaphore sem(0, 1);
|
||||||
|
Timeout timeout;
|
||||||
|
if (send_reset_notification(¤t_case, 2 * TIMEOUT_MS) == false) {
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Dev-host communication error.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config));
|
||||||
|
sleep_manager_lock_deep_sleep();
|
||||||
|
// Watchdog should fire before twice the timeout value.
|
||||||
|
timeout.attach_us(mbed::callback(release_sem, &sem), 1000ULL * (2 * TIMEOUT_MS));
|
||||||
|
if (sleep_manager_can_deep_sleep()) {
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Deepsleep should be disallowed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sem.wait(); // Device reset expected.
|
||||||
|
sleep_manager_unlock_deep_sleep();
|
||||||
|
|
||||||
|
// Watchdog reset should have occurred during sem.wait() (sleep) above;
|
||||||
|
|
||||||
|
hal_watchdog_kick(); // Just to buy some time for testsuite failure handling.
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEVICE_LOWPOWERTIMER
|
||||||
|
void test_deepsleep_reset()
|
||||||
|
{
|
||||||
|
// Phase 2. -- verify the test results.
|
||||||
|
if (current_case.received_data != CASE_DATA_INVALID) {
|
||||||
|
TEST_ASSERT_EQUAL(CASE_DATA_PHASE2_OK, current_case.received_data);
|
||||||
|
current_case.received_data = CASE_DATA_INVALID;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 1. -- run the test code.
|
||||||
|
watchdog_config_t config = { TIMEOUT_MS };
|
||||||
|
Semaphore sem(0, 1);
|
||||||
|
LowPowerTimeout lp_timeout;
|
||||||
|
if (send_reset_notification(¤t_case, 2 * TIMEOUT_MS) == false) {
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Dev-host communication error.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config));
|
||||||
|
// Watchdog should fire before twice the timeout value.
|
||||||
|
lp_timeout.attach_us(mbed::callback(release_sem, &sem), 1000ULL * (2 * TIMEOUT_MS));
|
||||||
|
wait_ms(SERIAL_FLUSH_TIME_MS); // Wait for the serial buffers to flush.
|
||||||
|
if (!sleep_manager_can_deep_sleep()) {
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Deepsleep should be allowed.");
|
||||||
|
}
|
||||||
|
sem.wait(); // Device reset expected.
|
||||||
|
|
||||||
|
// Watchdog reset should have occurred during sem.wait() (deepsleep) above;
|
||||||
|
|
||||||
|
hal_watchdog_kick(); // Just to buy some time for testsuite failure handling.
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected.");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void test_restart_reset()
|
||||||
|
{
|
||||||
|
watchdog_features_t features = hal_watchdog_get_platform_features();
|
||||||
|
if (!features.disable_watchdog) {
|
||||||
|
TEST_IGNORE_MESSAGE("Disabling Watchdog not supported for this platform");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 2. -- verify the test results.
|
||||||
|
if (current_case.received_data != CASE_DATA_INVALID) {
|
||||||
|
TEST_ASSERT_EQUAL(CASE_DATA_PHASE2_OK, current_case.received_data);
|
||||||
|
current_case.received_data = CASE_DATA_INVALID;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 1. -- run the test code.
|
||||||
|
watchdog_config_t config = { TIMEOUT_MS };
|
||||||
|
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config));
|
||||||
|
wait_ms(TIMEOUT_MS / 2UL);
|
||||||
|
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_stop());
|
||||||
|
// Check that stopping the Watchdog prevents a device reset.
|
||||||
|
// The watchdog should trigger at, or after the timeout value.
|
||||||
|
// The watchdog should trigger before twice the timeout value.
|
||||||
|
wait_ms(TIMEOUT_MS / 2UL + TIMEOUT_MS);
|
||||||
|
|
||||||
|
if (send_reset_notification(¤t_case, 2 * TIMEOUT_MS) == false) {
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Dev-host communication error.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config));
|
||||||
|
// Watchdog should fire before twice the timeout value.
|
||||||
|
wait_ms(2 * TIMEOUT_MS); // Device reset expected.
|
||||||
|
|
||||||
|
// Watchdog reset should have occurred during that wait() above;
|
||||||
|
|
||||||
|
hal_watchdog_kick(); // Just to buy some time for testsuite failure handling.
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_kick_reset()
|
||||||
|
{
|
||||||
|
// Phase 2. -- verify the test results.
|
||||||
|
if (current_case.received_data != CASE_DATA_INVALID) {
|
||||||
|
TEST_ASSERT_EQUAL(CASE_DATA_PHASE2_OK, current_case.received_data);
|
||||||
|
current_case.received_data = CASE_DATA_INVALID;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 1. -- run the test code.
|
||||||
|
watchdog_config_t config = { TIMEOUT_MS };
|
||||||
|
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config));
|
||||||
|
for (int i = 3; i; i--) {
|
||||||
|
// The reset is prevented as long as the watchdog is kicked
|
||||||
|
// anytime before the timeout.
|
||||||
|
wait_ms(TIMEOUT_MS - KICK_ADVANCE_MS);
|
||||||
|
hal_watchdog_kick();
|
||||||
|
}
|
||||||
|
if (send_reset_notification(¤t_case, 2 * TIMEOUT_MS) == false) {
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Dev-host communication error.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Watchdog should fire before twice the timeout value.
|
||||||
|
wait_ms(2 * TIMEOUT_MS); // Device reset expected.
|
||||||
|
|
||||||
|
// Watchdog reset should have occurred during that wait() above;
|
||||||
|
|
||||||
|
hal_watchdog_kick(); // Just to buy some time for testsuite failure handling.
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
utest::v1::status_t case_setup(const Case *const source, const size_t index_of_case)
|
||||||
|
{
|
||||||
|
current_case.index = index_of_case;
|
||||||
|
return utest::v1::greentea_case_setup_handler(source, index_of_case);
|
||||||
|
}
|
||||||
|
|
||||||
|
int testsuite_setup(const size_t number_of_cases)
|
||||||
|
{
|
||||||
|
GREENTEA_SETUP(90, "watchdog_reset");
|
||||||
|
utest::v1::status_t status = utest::v1::greentea_test_setup_handler(number_of_cases);
|
||||||
|
if (status != utest::v1::STATUS_CONTINUE) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
char key[MSG_KEY_LEN + 1] = { };
|
||||||
|
char value[MSG_VALUE_LEN + 1] = { };
|
||||||
|
|
||||||
|
greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_DUMMY);
|
||||||
|
greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN);
|
||||||
|
|
||||||
|
if (strcmp(key, MSG_KEY_START_CASE) != 0) {
|
||||||
|
utest_printf("Invalid message key.\n");
|
||||||
|
return utest::v1::STATUS_ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int num_args = sscanf(value, "%02x,%08lx", &(current_case.start_index), &(current_case.received_data));
|
||||||
|
if (num_args == 0 || num_args == EOF) {
|
||||||
|
utest_printf("Invalid data received from host\n");
|
||||||
|
return utest::v1::STATUS_ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
utest_printf("This test suite is composed of %i test cases. Starting at index %i.\n", number_of_cases,
|
||||||
|
current_case.start_index);
|
||||||
|
return current_case.start_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
Case cases[] = {
|
||||||
|
Case("Watchdog reset", case_setup, test_simple_reset),
|
||||||
|
#if DEVICE_SLEEP
|
||||||
|
Case("Watchdog reset in sleep mode", case_setup, test_sleep_reset),
|
||||||
|
#if DEVICE_LOWPOWERTIMER
|
||||||
|
Case("Watchdog reset in deepsleep mode", case_setup, test_deepsleep_reset),
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
Case("Watchdog started again", case_setup, test_restart_reset),
|
||||||
|
Case("Kicking the Watchdog prevents reset", case_setup, test_kick_reset),
|
||||||
|
};
|
||||||
|
|
||||||
|
Specification specification((utest::v1::test_setup_handler_t) testsuite_setup, cases);
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Harness will start with a test case index provided by host script.
|
||||||
|
return !Harness::run(specification);
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hal_watchdog_tests
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MBED_HAL_WATCHDOG_RESET_TESTS_H
|
||||||
|
#define MBED_HAL_WATCHDOG_RESET_TESTS_H
|
||||||
|
|
||||||
|
#if DEVICE_WATCHDOG
|
||||||
|
|
||||||
|
/** Test watchdog reset
|
||||||
|
*
|
||||||
|
* Given a device with a watchdog started,
|
||||||
|
* when a watchdog timeout expires,
|
||||||
|
* then the device is restarted.
|
||||||
|
*/
|
||||||
|
void test_simple_reset();
|
||||||
|
|
||||||
|
/** Test watchdog reset in sleep mode
|
||||||
|
*
|
||||||
|
* Given a device with a watchdog started,
|
||||||
|
* when the watchdog timeout expires while the device is in sleep mode,
|
||||||
|
* then the device is restarted.
|
||||||
|
*/
|
||||||
|
void test_sleep_reset();
|
||||||
|
|
||||||
|
/** Test watchdog reset in deepsleep mode
|
||||||
|
*
|
||||||
|
* Given a device with a watchdog started,
|
||||||
|
* when the watchdog timeout expires while the device is in deepsleep mode,
|
||||||
|
* then the device is restarted.
|
||||||
|
*/
|
||||||
|
void test_deepsleep_reset();
|
||||||
|
|
||||||
|
/** Test watchdog reset after watchdog restart
|
||||||
|
*
|
||||||
|
* Given a device with a watchdog started,
|
||||||
|
* when the watchdog is stopped before its timeout expires,
|
||||||
|
* then the device is not restarted.
|
||||||
|
* When the watchdog is started again and its timeout expires,
|
||||||
|
* then the device is restarted.
|
||||||
|
*/
|
||||||
|
void test_restart_reset();
|
||||||
|
|
||||||
|
/** Test watchdog kick
|
||||||
|
*
|
||||||
|
* Given a device with a watchdog started,
|
||||||
|
* when the watchdog is kicked before its timeout expires,
|
||||||
|
* then the device restart is prevented.
|
||||||
|
* When the watchdog is *NOT* kicked again before next timeout expires,
|
||||||
|
* then the device is restarted.
|
||||||
|
*/
|
||||||
|
void test_kick_reset();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @}*/
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#if !DEVICE_WATCHDOG
|
||||||
|
#error [NOT_SUPPORTED] Watchdog not supported for this target
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "greentea-client/test_env.h"
|
||||||
|
#include "hal/watchdog_api.h"
|
||||||
|
#include "unity/unity.h"
|
||||||
|
#include "us_ticker_api.h"
|
||||||
|
#include "utest/utest.h"
|
||||||
|
#include "watchdog_timing_tests.h"
|
||||||
|
|
||||||
|
#define MSG_VALUE_DUMMY "0"
|
||||||
|
#define CASE_DATA_INVALID 0xffffffffUL
|
||||||
|
|
||||||
|
#define MSG_VALUE_LEN 24
|
||||||
|
#define MSG_KEY_LEN 24
|
||||||
|
|
||||||
|
#define MSG_KEY_DEVICE_READY "ready"
|
||||||
|
#define MSG_KEY_START_CASE "start_case"
|
||||||
|
#define MSG_KEY_HEARTBEAT "hb"
|
||||||
|
|
||||||
|
using utest::v1::Case;
|
||||||
|
using utest::v1::Specification;
|
||||||
|
using utest::v1::Harness;
|
||||||
|
|
||||||
|
struct testcase_data {
|
||||||
|
int index;
|
||||||
|
int start_index;
|
||||||
|
uint32_t received_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
testcase_data current_case;
|
||||||
|
|
||||||
|
template<uint32_t timeout_ms>
|
||||||
|
void test_timing()
|
||||||
|
{
|
||||||
|
watchdog_features_t features = hal_watchdog_get_platform_features();
|
||||||
|
if (timeout_ms > features.max_timeout) {
|
||||||
|
TEST_IGNORE_MESSAGE("Requested timeout value not supported for this target -- ignoring test case.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 2. -- verify the test results.
|
||||||
|
// Verify the heartbeat time span sent by host is within given range:
|
||||||
|
// 1. The watchdog should trigger at, or after the timeout value.
|
||||||
|
// 2. The watchdog should trigger before twice the timeout value.
|
||||||
|
if (current_case.received_data != CASE_DATA_INVALID) {
|
||||||
|
// Provided the watchdog works as expected, the last timestamp received
|
||||||
|
// by the host will always be before the expected reset time. Because
|
||||||
|
// of that, the constraint no 1. is not verified.
|
||||||
|
TEST_ASSERT(current_case.received_data > 0);
|
||||||
|
TEST_ASSERT(current_case.received_data < 2 * timeout_ms);
|
||||||
|
current_case.received_data = CASE_DATA_INVALID;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 1. -- run the test code.
|
||||||
|
// Send heartbeat messages to host until the watchdeg resets the device.
|
||||||
|
const ticker_data_t *const us_ticker = get_us_ticker_data();
|
||||||
|
us_timestamp_t current_ts, next_ts, expected_reset_ts, divider, ts_increment;
|
||||||
|
char msg_value[12];
|
||||||
|
|
||||||
|
watchdog_config_t config = { timeout_ms };
|
||||||
|
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config));
|
||||||
|
next_ts = ticker_read_us(us_ticker);
|
||||||
|
expected_reset_ts = next_ts + 1000ULL * timeout_ms;
|
||||||
|
|
||||||
|
divider = 0x2ULL;
|
||||||
|
while (1) {
|
||||||
|
current_ts = ticker_read_us(us_ticker);
|
||||||
|
if (current_ts < next_ts) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int str_len = snprintf(msg_value, sizeof msg_value, "%02x,%08lx", current_case.start_index + current_case.index,
|
||||||
|
(uint32_t) current_ts);
|
||||||
|
if (str_len != (sizeof msg_value) - 1) {
|
||||||
|
utest_printf("Failed to compose a value string to be sent to host.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
greentea_send_kv(MSG_KEY_HEARTBEAT, msg_value);
|
||||||
|
|
||||||
|
// The closer to expected reset, the smaller heartbeat time difference.
|
||||||
|
// This should reduce measurement error present for heartbeat with
|
||||||
|
// equal periods.
|
||||||
|
ts_increment = (1000ULL * timeout_ms / divider);
|
||||||
|
next_ts += ts_increment;
|
||||||
|
|
||||||
|
if (current_ts <= expected_reset_ts) {
|
||||||
|
divider <<= 1;
|
||||||
|
} else if (divider > 0x2ULL) {
|
||||||
|
divider >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
utest::v1::status_t case_setup(const Case *const source, const size_t index_of_case)
|
||||||
|
{
|
||||||
|
current_case.index = index_of_case;
|
||||||
|
return utest::v1::greentea_case_setup_handler(source, index_of_case);
|
||||||
|
}
|
||||||
|
|
||||||
|
int testsuite_setup(const size_t number_of_cases)
|
||||||
|
{
|
||||||
|
GREENTEA_SETUP(90, "watchdog_reset");
|
||||||
|
utest::v1::status_t status = utest::v1::greentea_test_setup_handler(number_of_cases);
|
||||||
|
if (status != utest::v1::STATUS_CONTINUE) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
char key[MSG_KEY_LEN + 1] = { };
|
||||||
|
char value[MSG_VALUE_LEN + 1] = { };
|
||||||
|
|
||||||
|
greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_DUMMY);
|
||||||
|
greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN);
|
||||||
|
|
||||||
|
if (strcmp(key, MSG_KEY_START_CASE) != 0) {
|
||||||
|
utest_printf("Invalid message key.\n");
|
||||||
|
return utest::v1::STATUS_ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int num_args = sscanf(value, "%02x,%08lx", &(current_case.start_index), &(current_case.received_data));
|
||||||
|
if (num_args == 0 || num_args == EOF) {
|
||||||
|
utest_printf("Invalid data received from host\n");
|
||||||
|
return utest::v1::STATUS_ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
utest_printf("This test suite is composed of %i test cases. Starting at index %i.\n", number_of_cases,
|
||||||
|
current_case.start_index);
|
||||||
|
return current_case.start_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
Case cases[] = {
|
||||||
|
Case("Timing, 200 ms", case_setup, test_timing<200UL>),
|
||||||
|
Case("Timing, 500 ms", case_setup, test_timing<500UL>),
|
||||||
|
Case("Timing, 1000 ms", case_setup, test_timing<1000UL>),
|
||||||
|
Case("Timing, 3000 ms", case_setup, test_timing<3000UL>),
|
||||||
|
};
|
||||||
|
|
||||||
|
Specification specification((utest::v1::test_setup_handler_t) testsuite_setup, cases);
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Harness will start with a test case index provided by host script.
|
||||||
|
return !Harness::run(specification);
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup hal_watchdog_tests
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MBED_HAL_WATCHDOG_TIMING_TESTS_H
|
||||||
|
#define MBED_HAL_WATCHDOG_TIMING_TESTS_H
|
||||||
|
|
||||||
|
#if DEVICE_WATCHDOG
|
||||||
|
|
||||||
|
/** Test watchdog timing accuracy
|
||||||
|
*
|
||||||
|
* Phase 1.
|
||||||
|
* Given a watchdog timer started with a timeout value of X ms,
|
||||||
|
* when the time of X ms elapses,
|
||||||
|
* then the device is restarted by the watchdog.
|
||||||
|
*
|
||||||
|
* Phase 2.
|
||||||
|
* Given a device restarted by the watchdog timer,
|
||||||
|
* when the device receives time measurement T from the host,
|
||||||
|
* then X <= T < 2 * X.
|
||||||
|
*/
|
||||||
|
template<uint32_t timeout_ms, uint32_t delta_ms>
|
||||||
|
void test_timing();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @}*/
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !DEVICE_ANALOGIN
|
||||||
|
#error [NOT_SUPPORTED] Analog in not supported for this target
|
||||||
|
#elif !COMPONENT_FPGA_CI_TEST_SHIELD
|
||||||
|
#error [NOT_SUPPORTED] FPGA CI Test Shield is needed to run this test
|
||||||
|
#elif !defined(TARGET_FF_ARDUINO) && !defined(MBED_CONF_TARGET_DEFAULT_FORM_FACTOR)
|
||||||
|
#error [NOT_SUPPORTED] Test not supported for this form factor
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include "utest/utest.h"
|
||||||
|
#include "unity/unity.h"
|
||||||
|
#include "greentea-client/test_env.h"
|
||||||
|
|
||||||
|
#include "mbed.h"
|
||||||
|
#include "pinmap.h"
|
||||||
|
#include "test_utils.h"
|
||||||
|
#include "MbedTester.h"
|
||||||
|
|
||||||
|
using namespace utest::v1;
|
||||||
|
|
||||||
|
#define analogin_debug_printf(...)
|
||||||
|
|
||||||
|
#define DELTA_FLOAT 0.03f // 3%
|
||||||
|
#define DELTA_U16 1965 // 3%
|
||||||
|
|
||||||
|
const PinList *form_factor = pinmap_ff_default_pins();
|
||||||
|
const PinList *restricted = pinmap_restricted_pins();
|
||||||
|
|
||||||
|
MbedTester tester(form_factor, restricted);
|
||||||
|
|
||||||
|
void analogin_init(PinName pin)
|
||||||
|
{
|
||||||
|
analogin_t analogin;
|
||||||
|
|
||||||
|
analogin_init(&analogin, pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void analogin_test(PinName pin)
|
||||||
|
{
|
||||||
|
tester.reset();
|
||||||
|
tester.pin_map_set(pin, MbedTester::LogicalPinGPIO0);
|
||||||
|
tester.select_peripheral(MbedTester::PeripheralGPIO);
|
||||||
|
|
||||||
|
/* Test analog input */
|
||||||
|
|
||||||
|
analogin_t analogin;
|
||||||
|
analogin_init(&analogin, pin);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
TEST_ASSERT_FLOAT_WITHIN(DELTA_FLOAT, 1.0f, analogin_read(&analogin));
|
||||||
|
TEST_ASSERT_UINT16_WITHIN(DELTA_U16, 65535, analogin_read_u16(&analogin));
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
TEST_ASSERT_FLOAT_WITHIN(DELTA_FLOAT, 0.0f, analogin_read(&analogin));
|
||||||
|
TEST_ASSERT_UINT16_WITHIN(DELTA_U16, 0, analogin_read_u16(&analogin));
|
||||||
|
|
||||||
|
/* Set gpio back to Hi-Z */
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Case cases[] = {
|
||||||
|
// This will be run for all pins
|
||||||
|
Case("AnalogIn - init test", all_ports<AnaloginPort, DefaultFormFactor, analogin_init>),
|
||||||
|
// This will be run for single pin
|
||||||
|
Case("AnalogIn - read test", all_ports<AnaloginPort, DefaultFormFactor, analogin_test>),
|
||||||
|
};
|
||||||
|
|
||||||
|
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
||||||
|
{
|
||||||
|
GREENTEA_SETUP(120, "default_auto");
|
||||||
|
return greentea_test_setup_handler(number_of_cases);
|
||||||
|
}
|
||||||
|
|
||||||
|
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
Harness::run(specification);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !DEVICE_ANALOGIN */
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !COMPONENT_FPGA_CI_TEST_SHIELD
|
||||||
|
#error [NOT_SUPPORTED] FPGA CI Test Shield is needed to run this test
|
||||||
|
#elif !defined(TARGET_FF_ARDUINO) && !defined(MBED_CONF_TARGET_DEFAULT_FORM_FACTOR)
|
||||||
|
#error [NOT_SUPPORTED] Test not supported for this form factor
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include "utest/utest.h"
|
||||||
|
#include "unity/unity.h"
|
||||||
|
#include "greentea-client/test_env.h"
|
||||||
|
#include "mbed.h"
|
||||||
|
|
||||||
|
using namespace utest::v1;
|
||||||
|
|
||||||
|
#include "MbedTester.h"
|
||||||
|
#include "pinmap.h"
|
||||||
|
|
||||||
|
const PinList *form_factor = pinmap_ff_default_pins();
|
||||||
|
const PinList *restricted = pinmap_restricted_pins();
|
||||||
|
MbedTester tester(form_factor, restricted);
|
||||||
|
|
||||||
|
void gpio_inout_test(PinName pin)
|
||||||
|
{
|
||||||
|
gpio_t gpio;
|
||||||
|
gpio_init_inout(&gpio, pin, PIN_OUTPUT, PullNone, 0);
|
||||||
|
TEST_ASSERT_EQUAL(0, tester.gpio_read(MbedTester::LogicalPinGPIO0));
|
||||||
|
|
||||||
|
/* Test GPIO output */
|
||||||
|
|
||||||
|
gpio_write(&gpio, 1);
|
||||||
|
TEST_ASSERT_EQUAL(1, tester.gpio_read(MbedTester::LogicalPinGPIO0));
|
||||||
|
|
||||||
|
gpio_write(&gpio, 0);
|
||||||
|
TEST_ASSERT_EQUAL(0, tester.gpio_read(MbedTester::LogicalPinGPIO0));
|
||||||
|
|
||||||
|
gpio_dir(&gpio, PIN_INPUT);
|
||||||
|
|
||||||
|
/* Test GPIO input */
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
TEST_ASSERT_EQUAL(0, gpio_read(&gpio));
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
TEST_ASSERT_EQUAL(1, gpio_read(&gpio));
|
||||||
|
|
||||||
|
/* Set gpio back to Hi-Z */
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gpio_inout_test()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < form_factor->count; i++) {
|
||||||
|
const PinName test_pin = form_factor->pins[i];
|
||||||
|
if (test_pin == NC) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (pinmap_list_has_pin(restricted, test_pin)) {
|
||||||
|
printf("Skipping gpio pin %s (%i)\r\n", pinmap_ff_default_pin_to_string(test_pin), test_pin);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tester.pin_map_reset();
|
||||||
|
tester.pin_map_set(test_pin, MbedTester::LogicalPinGPIO0);
|
||||||
|
|
||||||
|
printf("GPIO test on pin %s (%i)\r\n", pinmap_ff_default_pin_to_string(test_pin), test_pin);
|
||||||
|
gpio_inout_test(test_pin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
utest::v1::status_t setup(const Case *const source, const size_t index_of_case)
|
||||||
|
{
|
||||||
|
tester.reset();
|
||||||
|
tester.select_peripheral(MbedTester::PeripheralGPIO);
|
||||||
|
|
||||||
|
return greentea_case_setup_handler(source, index_of_case);
|
||||||
|
}
|
||||||
|
|
||||||
|
utest::v1::status_t teardown(const Case *const source, const size_t passed, const size_t failed,
|
||||||
|
const failure_t reason)
|
||||||
|
{
|
||||||
|
return greentea_case_teardown_handler(source, passed, failed, reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
Case cases[] = {
|
||||||
|
Case("GPIO - inout", setup, gpio_inout_test, teardown),
|
||||||
|
};
|
||||||
|
|
||||||
|
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
||||||
|
{
|
||||||
|
GREENTEA_SETUP(60, "default_auto");
|
||||||
|
return greentea_test_setup_handler(number_of_cases);
|
||||||
|
}
|
||||||
|
|
||||||
|
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
Harness::run(specification);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !COMPONENT_FPGA_CI_TEST_SHIELD */
|
|
@ -0,0 +1,299 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !DEVICE_INTERRUPTIN
|
||||||
|
#error [NOT_SUPPORTED] test not supported
|
||||||
|
#elif !COMPONENT_FPGA_CI_TEST_SHIELD
|
||||||
|
#error [NOT_SUPPORTED] FPGA CI Test Shield is needed to run this test
|
||||||
|
#elif !defined(TARGET_FF_ARDUINO) && !defined(MBED_CONF_TARGET_DEFAULT_FORM_FACTOR)
|
||||||
|
#error [NOT_SUPPORTED] Test not supported for this form factor
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include "utest/utest.h"
|
||||||
|
#include "unity/unity.h"
|
||||||
|
#include "greentea-client/test_env.h"
|
||||||
|
#include "mbed.h"
|
||||||
|
|
||||||
|
using namespace utest::v1;
|
||||||
|
|
||||||
|
#include "mbed.h"
|
||||||
|
#include "MbedTester.h"
|
||||||
|
#include "pinmap.h"
|
||||||
|
|
||||||
|
static uint32_t call_counter;
|
||||||
|
void test_gpio_irq_handler(uint32_t id, gpio_irq_event event)
|
||||||
|
{
|
||||||
|
call_counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PinList *form_factor = pinmap_ff_default_pins();
|
||||||
|
const PinList *restricted = pinmap_restricted_pins();
|
||||||
|
MbedTester tester(form_factor, restricted);
|
||||||
|
|
||||||
|
#define WAIT() wait_us(10)
|
||||||
|
|
||||||
|
|
||||||
|
void gpio_irq_test(PinName pin)
|
||||||
|
{
|
||||||
|
gpio_t gpio;
|
||||||
|
// configure pin as input
|
||||||
|
gpio_init_in(&gpio, pin);
|
||||||
|
|
||||||
|
gpio_irq_t gpio_irq;
|
||||||
|
uint32_t id = 123;
|
||||||
|
gpio_irq_init(&gpio_irq, pin, test_gpio_irq_handler, id);
|
||||||
|
|
||||||
|
gpio_irq_set(&gpio_irq, IRQ_RISE, true);
|
||||||
|
gpio_irq_enable(&gpio_irq);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
|
||||||
|
// test irq on rising edge
|
||||||
|
call_counter = 0;
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(1, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(1, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(2, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(2, call_counter);
|
||||||
|
|
||||||
|
gpio_irq_disable(&gpio_irq);
|
||||||
|
|
||||||
|
call_counter = 0;
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(0, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(0, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(0, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(0, call_counter);
|
||||||
|
|
||||||
|
gpio_irq_enable(&gpio_irq);
|
||||||
|
|
||||||
|
call_counter = 0;
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(1, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(1, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(2, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(2, call_counter);
|
||||||
|
|
||||||
|
// test irq on both rising and falling edge
|
||||||
|
gpio_irq_set(&gpio_irq, IRQ_FALL, true);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
|
||||||
|
call_counter = 0;
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(1, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(2, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(3, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(4, call_counter);
|
||||||
|
|
||||||
|
gpio_irq_disable(&gpio_irq);
|
||||||
|
|
||||||
|
call_counter = 0;
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(0, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(0, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(0, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(0, call_counter);
|
||||||
|
|
||||||
|
gpio_irq_enable(&gpio_irq);
|
||||||
|
|
||||||
|
call_counter = 0;
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(1, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(2, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(3, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(4, call_counter);
|
||||||
|
|
||||||
|
// test irq on falling edge
|
||||||
|
gpio_irq_set(&gpio_irq, IRQ_RISE, false);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
|
||||||
|
call_counter = 0;
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(1, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(1, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(2, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(2, call_counter);
|
||||||
|
|
||||||
|
gpio_irq_disable(&gpio_irq);
|
||||||
|
|
||||||
|
call_counter = 0;
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(0, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(0, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(0, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(0, call_counter);
|
||||||
|
|
||||||
|
gpio_irq_enable(&gpio_irq);
|
||||||
|
|
||||||
|
call_counter = 0;
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(1, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(1, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 0, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(2, call_counter);
|
||||||
|
|
||||||
|
tester.gpio_write(MbedTester::LogicalPinGPIO0, 1, true);
|
||||||
|
WAIT();
|
||||||
|
TEST_ASSERT_EQUAL(2, call_counter);
|
||||||
|
|
||||||
|
|
||||||
|
gpio_irq_free(&gpio_irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gpio_irq_test()
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < form_factor->count; i++) {
|
||||||
|
const PinName test_pin = form_factor->pins[i];
|
||||||
|
if (test_pin == NC) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (pinmap_list_has_pin(restricted, test_pin)) {
|
||||||
|
printf("Skipping gpio pin %s (%i)\r\n", pinmap_ff_default_pin_to_string(test_pin), test_pin);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tester.pin_map_reset();
|
||||||
|
tester.pin_map_set(test_pin, MbedTester::LogicalPinGPIO0);
|
||||||
|
|
||||||
|
printf("GPIO irq test on pin %3s (%3i)\r\n", pinmap_ff_default_pin_to_string(test_pin), test_pin);
|
||||||
|
gpio_irq_test(test_pin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
utest::v1::status_t setup(const Case *const source, const size_t index_of_case)
|
||||||
|
{
|
||||||
|
tester.reset();
|
||||||
|
tester.select_peripheral(MbedTester::PeripheralGPIO);
|
||||||
|
|
||||||
|
return greentea_case_setup_handler(source, index_of_case);
|
||||||
|
}
|
||||||
|
|
||||||
|
utest::v1::status_t teardown(const Case *const source, const size_t passed, const size_t failed,
|
||||||
|
const failure_t reason)
|
||||||
|
{
|
||||||
|
return greentea_case_teardown_handler(source, passed, failed, reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
Case cases[] = {
|
||||||
|
Case("GPIO - irq test", setup, gpio_irq_test, teardown)
|
||||||
|
};
|
||||||
|
|
||||||
|
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
||||||
|
{
|
||||||
|
GREENTEA_SETUP(10, "default_auto");
|
||||||
|
return greentea_test_setup_handler(number_of_cases);
|
||||||
|
}
|
||||||
|
|
||||||
|
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
Harness::run(specification);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !DEVICE_INTERRUPTIN */
|
|
@ -0,0 +1,446 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !DEVICE_I2C
|
||||||
|
#error [NOT_SUPPORTED] I2C not supported for this target
|
||||||
|
#elif !COMPONENT_FPGA_CI_TEST_SHIELD
|
||||||
|
#error [NOT_SUPPORTED] FPGA CI Test Shield is needed to run this test
|
||||||
|
#elif !defined(TARGET_FF_ARDUINO) && !defined(MBED_CONF_TARGET_DEFAULT_FORM_FACTOR)
|
||||||
|
#error [NOT_SUPPORTED] Test not supported for this form factor
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include "utest/utest.h"
|
||||||
|
#include "unity/unity.h"
|
||||||
|
#include "greentea-client/test_env.h"
|
||||||
|
#include "mbed.h"
|
||||||
|
|
||||||
|
#include "mbed.h"
|
||||||
|
#include "i2c_api.h"
|
||||||
|
#include "pinmap.h"
|
||||||
|
#include "test_utils.h"
|
||||||
|
#include "I2CTester.h"
|
||||||
|
|
||||||
|
|
||||||
|
using namespace utest::v1;
|
||||||
|
|
||||||
|
#define NACK 0
|
||||||
|
#define ACK 1
|
||||||
|
#define TIMEOUT 2
|
||||||
|
#define I2C_DEV_ADDR 0x98//default i2c slave address on FPGA is 0x98 until modified
|
||||||
|
const int TRANSFER_COUNT = 300;
|
||||||
|
|
||||||
|
I2CTester tester(DefaultFormFactor::pins(), DefaultFormFactor::restricted_pins());
|
||||||
|
|
||||||
|
void test_i2c_init_free(PinName sda, PinName scl)
|
||||||
|
{
|
||||||
|
i2c_t obj = {};
|
||||||
|
memset(&obj, 0, sizeof(obj));
|
||||||
|
i2c_init(&obj, sda, scl);
|
||||||
|
i2c_frequency(&obj, 100000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void i2c_test_write(PinName sda, PinName scl)
|
||||||
|
{
|
||||||
|
// Remap pins for test
|
||||||
|
tester.reset();
|
||||||
|
tester.pin_map_set(sda, MbedTester::LogicalPinI2CSda);
|
||||||
|
tester.pin_map_set(scl, MbedTester::LogicalPinI2CScl);
|
||||||
|
|
||||||
|
tester.pin_set_pull(sda, MbedTester::PullUp);
|
||||||
|
tester.pin_set_pull(scl, MbedTester::PullUp);
|
||||||
|
|
||||||
|
// Initialize mbed I2C pins
|
||||||
|
i2c_t i2c;
|
||||||
|
memset(&i2c, 0, sizeof(i2c));
|
||||||
|
i2c_init(&i2c, sda, scl);
|
||||||
|
i2c_frequency(&i2c, 100000);
|
||||||
|
|
||||||
|
// Reset tester stats and select I2C
|
||||||
|
tester.peripherals_reset();
|
||||||
|
tester.select_peripheral(I2CTester::PeripheralI2C);
|
||||||
|
|
||||||
|
// Data out and in buffers and initialization
|
||||||
|
uint8_t data_out[TRANSFER_COUNT];
|
||||||
|
for (int i = 0; i < TRANSFER_COUNT; i++) {
|
||||||
|
data_out[i] = i & 0xFF;
|
||||||
|
}
|
||||||
|
uint8_t data_in[TRANSFER_COUNT];
|
||||||
|
for (int i = 0; i < TRANSFER_COUNT; i++) {
|
||||||
|
data_in[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int num_writes;
|
||||||
|
int num_reads;
|
||||||
|
int num_acks;
|
||||||
|
int num_nacks;
|
||||||
|
int num_starts;
|
||||||
|
int num_stops;
|
||||||
|
uint32_t checksum;
|
||||||
|
int num_dev_addr_matches;
|
||||||
|
int ack_nack;//0 if NACK was received, 1 if ACK was received, 2 for timeout
|
||||||
|
|
||||||
|
// Reset tester stats and select I2C
|
||||||
|
tester.peripherals_reset();
|
||||||
|
tester.select_peripheral(MbedTester::PeripheralI2C);
|
||||||
|
|
||||||
|
// Write data for I2C complete transaction
|
||||||
|
// Will write 0-(TRANSFER_COUNT-1) to FPGA, checksum must match checksum calculated in parallel on FPGA
|
||||||
|
num_dev_addr_matches = 0;
|
||||||
|
num_writes = 0;
|
||||||
|
num_reads = 0;
|
||||||
|
num_starts = 0;
|
||||||
|
num_stops = 0;
|
||||||
|
num_acks = 0;
|
||||||
|
num_nacks = 0;
|
||||||
|
checksum = 0;
|
||||||
|
|
||||||
|
num_writes = i2c_write(&i2c, I2C_DEV_ADDR, (char *)data_out, TRANSFER_COUNT, true); //transaction ends with a stop condition
|
||||||
|
num_acks = num_writes + 1;
|
||||||
|
num_starts += 1;
|
||||||
|
num_stops += 1;
|
||||||
|
num_dev_addr_matches += 1;
|
||||||
|
|
||||||
|
for (int i = 0; i < TRANSFER_COUNT; i++) {
|
||||||
|
checksum += data_out[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the transfer was successful
|
||||||
|
TEST_ASSERT_EQUAL(num_dev_addr_matches, tester.num_dev_addr_matches());
|
||||||
|
TEST_ASSERT_EQUAL(TRANSFER_COUNT, num_writes);
|
||||||
|
TEST_ASSERT_EQUAL(num_writes + 1, tester.transfer_count());
|
||||||
|
TEST_ASSERT_EQUAL(num_starts, tester.num_starts());
|
||||||
|
TEST_ASSERT_EQUAL(num_stops, tester.num_stops());
|
||||||
|
TEST_ASSERT_EQUAL(num_acks, tester.num_acks());
|
||||||
|
TEST_ASSERT_EQUAL(num_nacks, tester.num_nacks());
|
||||||
|
TEST_ASSERT_EQUAL(checksum, tester.get_receive_checksum());
|
||||||
|
TEST_ASSERT_EQUAL(0, tester.state_num());
|
||||||
|
TEST_ASSERT_EQUAL(data_out[TRANSFER_COUNT - 4], tester.get_prev_to_slave_4());
|
||||||
|
TEST_ASSERT_EQUAL(data_out[TRANSFER_COUNT - 3], tester.get_prev_to_slave_3());
|
||||||
|
TEST_ASSERT_EQUAL(data_out[TRANSFER_COUNT - 2], tester.get_prev_to_slave_2());
|
||||||
|
TEST_ASSERT_EQUAL(data_out[TRANSFER_COUNT - 1], tester.get_prev_to_slave_1());
|
||||||
|
TEST_ASSERT_EQUAL(num_writes, tester.num_writes());
|
||||||
|
TEST_ASSERT_EQUAL(num_reads, tester.num_reads());
|
||||||
|
|
||||||
|
tester.reset();
|
||||||
|
tester.pin_set_pull(sda, MbedTester::PullNone);
|
||||||
|
tester.pin_set_pull(scl, MbedTester::PullNone);
|
||||||
|
}
|
||||||
|
|
||||||
|
void i2c_test_read(PinName sda, PinName scl)
|
||||||
|
{
|
||||||
|
// Remap pins for test
|
||||||
|
tester.reset();
|
||||||
|
tester.pin_map_set(sda, MbedTester::LogicalPinI2CSda);
|
||||||
|
tester.pin_map_set(scl, MbedTester::LogicalPinI2CScl);
|
||||||
|
|
||||||
|
tester.pin_set_pull(sda, MbedTester::PullUp);
|
||||||
|
tester.pin_set_pull(scl, MbedTester::PullUp);
|
||||||
|
|
||||||
|
// Initialize mbed I2C pins
|
||||||
|
i2c_t i2c;
|
||||||
|
memset(&i2c, 0, sizeof(i2c));
|
||||||
|
i2c_init(&i2c, sda, scl);
|
||||||
|
i2c_frequency(&i2c, 100000);
|
||||||
|
|
||||||
|
// Reset tester stats and select I2C
|
||||||
|
tester.peripherals_reset();
|
||||||
|
tester.select_peripheral(I2CTester::PeripheralI2C);
|
||||||
|
|
||||||
|
// Data out and in buffers and initialization
|
||||||
|
uint8_t data_out[TRANSFER_COUNT];
|
||||||
|
for (int i = 0; i < TRANSFER_COUNT; i++) {
|
||||||
|
data_out[i] = i & 0xFF;
|
||||||
|
}
|
||||||
|
uint8_t data_in[TRANSFER_COUNT];
|
||||||
|
for (int i = 0; i < TRANSFER_COUNT; i++) {
|
||||||
|
data_in[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int num_writes;
|
||||||
|
int num_reads;
|
||||||
|
int num_acks;
|
||||||
|
int num_nacks;
|
||||||
|
int num_starts;
|
||||||
|
int num_stops;
|
||||||
|
uint32_t checksum;
|
||||||
|
int num_dev_addr_matches;
|
||||||
|
int ack_nack;//0 if NACK was received, 1 if ACK was received, 2 for timeout
|
||||||
|
|
||||||
|
// Reset tester stats and select I2C
|
||||||
|
tester.peripherals_reset();
|
||||||
|
tester.select_peripheral(MbedTester::PeripheralI2C);
|
||||||
|
|
||||||
|
// Read data for I2C complete transaction
|
||||||
|
// Will read <TRANSFER_COUNT> bytes, checksum must match checksum calculated in parallel on FPGA
|
||||||
|
num_dev_addr_matches = 0;
|
||||||
|
num_writes = 0;
|
||||||
|
num_reads = 0;
|
||||||
|
num_starts = 0;
|
||||||
|
num_stops = 0;
|
||||||
|
num_acks = 0;
|
||||||
|
num_nacks = 0;
|
||||||
|
checksum = 0;
|
||||||
|
|
||||||
|
num_reads = i2c_read(&i2c, (I2C_DEV_ADDR | 1), (char *)data_in, TRANSFER_COUNT, true); //transaction ends with a stop condition
|
||||||
|
num_starts += 1;
|
||||||
|
num_stops += 1;
|
||||||
|
num_acks += 1;
|
||||||
|
num_acks += TRANSFER_COUNT - 1;
|
||||||
|
num_nacks += 1;
|
||||||
|
num_dev_addr_matches += 1;
|
||||||
|
|
||||||
|
for (int i = 0; i < TRANSFER_COUNT; i++) {
|
||||||
|
checksum += data_in[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the transfer was successful
|
||||||
|
TEST_ASSERT_EQUAL(num_dev_addr_matches, tester.num_dev_addr_matches());
|
||||||
|
TEST_ASSERT_EQUAL(TRANSFER_COUNT, num_reads);
|
||||||
|
TEST_ASSERT_EQUAL(num_reads + 1, tester.transfer_count());
|
||||||
|
TEST_ASSERT_EQUAL(num_starts, tester.num_starts());
|
||||||
|
TEST_ASSERT_EQUAL(num_stops, tester.num_stops());
|
||||||
|
TEST_ASSERT_EQUAL(num_acks, tester.num_acks());
|
||||||
|
TEST_ASSERT_EQUAL(num_nacks, tester.num_nacks());
|
||||||
|
TEST_ASSERT_EQUAL(checksum, tester.get_send_checksum());
|
||||||
|
TEST_ASSERT_EQUAL(0, tester.state_num());
|
||||||
|
TEST_ASSERT_EQUAL(((TRANSFER_COUNT + 1) & 0xFF), tester.get_next_from_slave());
|
||||||
|
TEST_ASSERT_EQUAL(num_writes, tester.num_writes());
|
||||||
|
TEST_ASSERT_EQUAL(num_reads, tester.num_reads());
|
||||||
|
|
||||||
|
tester.reset();
|
||||||
|
tester.pin_set_pull(sda, MbedTester::PullNone);
|
||||||
|
tester.pin_set_pull(scl, MbedTester::PullNone);
|
||||||
|
}
|
||||||
|
|
||||||
|
void i2c_test_byte_write(PinName sda, PinName scl)
|
||||||
|
{
|
||||||
|
// Remap pins for test
|
||||||
|
tester.reset();
|
||||||
|
tester.pin_map_set(sda, MbedTester::LogicalPinI2CSda);
|
||||||
|
tester.pin_map_set(scl, MbedTester::LogicalPinI2CScl);
|
||||||
|
|
||||||
|
tester.pin_set_pull(sda, MbedTester::PullUp);
|
||||||
|
tester.pin_set_pull(scl, MbedTester::PullUp);
|
||||||
|
|
||||||
|
// Initialize mbed I2C pins
|
||||||
|
i2c_t i2c;
|
||||||
|
memset(&i2c, 0, sizeof(i2c));
|
||||||
|
i2c_init(&i2c, sda, scl);
|
||||||
|
i2c_frequency(&i2c, 100000);
|
||||||
|
|
||||||
|
// Reset tester stats and select I2C
|
||||||
|
tester.peripherals_reset();
|
||||||
|
tester.select_peripheral(I2CTester::PeripheralI2C);
|
||||||
|
|
||||||
|
// Data out and in buffers and initialization
|
||||||
|
uint8_t data_out[TRANSFER_COUNT];
|
||||||
|
for (int i = 0; i < TRANSFER_COUNT; i++) {
|
||||||
|
data_out[i] = i & 0xFF;
|
||||||
|
}
|
||||||
|
uint8_t data_in[TRANSFER_COUNT];
|
||||||
|
for (int i = 0; i < TRANSFER_COUNT; i++) {
|
||||||
|
data_in[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int num_writes;
|
||||||
|
int num_reads;
|
||||||
|
int num_acks;
|
||||||
|
int num_nacks;
|
||||||
|
int num_starts;
|
||||||
|
int num_stops;
|
||||||
|
uint32_t checksum;
|
||||||
|
int num_dev_addr_matches;
|
||||||
|
int ack_nack;//0 if NACK was received, 1 if ACK was received, 2 for timeout
|
||||||
|
|
||||||
|
// Reset tester stats and select I2C
|
||||||
|
tester.peripherals_reset();
|
||||||
|
tester.select_peripheral(MbedTester::PeripheralI2C);
|
||||||
|
|
||||||
|
// Write data for I2C single byte transfers
|
||||||
|
// Will write 0-(TRANSFER_COUNT-1) to FPGA, checksum must match checksum calculated in parallel on FPGA
|
||||||
|
num_dev_addr_matches = 0;
|
||||||
|
num_writes = 0;
|
||||||
|
num_reads = 0;
|
||||||
|
num_starts = 0;
|
||||||
|
num_stops = 0;
|
||||||
|
num_acks = 0;
|
||||||
|
num_nacks = 0;
|
||||||
|
checksum = 0;
|
||||||
|
|
||||||
|
i2c_start(&i2c);//start condition
|
||||||
|
num_starts += 1;
|
||||||
|
i2c_byte_write(&i2c, I2C_DEV_ADDR);//send device address
|
||||||
|
num_dev_addr_matches += 1;
|
||||||
|
num_acks += 1;
|
||||||
|
for (int i = 0; i < TRANSFER_COUNT; i++) {
|
||||||
|
ack_nack = i2c_byte_write(&i2c, data_out[i]);//send data
|
||||||
|
if (ack_nack == ACK) {
|
||||||
|
num_acks += 1;
|
||||||
|
} else if (ack_nack == NACK) {
|
||||||
|
num_nacks += 1;
|
||||||
|
} else {
|
||||||
|
printf("Timeout error\n\r");
|
||||||
|
}
|
||||||
|
checksum += data_out[i];
|
||||||
|
num_writes += 1;
|
||||||
|
}
|
||||||
|
i2c_stop(&i2c);
|
||||||
|
num_stops += 1;
|
||||||
|
|
||||||
|
// Verify that the transfer was successful
|
||||||
|
TEST_ASSERT_EQUAL(num_dev_addr_matches, tester.num_dev_addr_matches());
|
||||||
|
TEST_ASSERT_EQUAL(TRANSFER_COUNT, num_writes);
|
||||||
|
TEST_ASSERT_EQUAL(num_writes + 1, tester.transfer_count());
|
||||||
|
TEST_ASSERT_EQUAL(num_starts, tester.num_starts());
|
||||||
|
TEST_ASSERT_EQUAL(num_stops, tester.num_stops());
|
||||||
|
TEST_ASSERT_EQUAL(num_acks, tester.num_acks());
|
||||||
|
TEST_ASSERT_EQUAL(num_nacks, tester.num_nacks());
|
||||||
|
TEST_ASSERT_EQUAL(checksum, tester.get_receive_checksum());
|
||||||
|
TEST_ASSERT_EQUAL(0, tester.state_num());
|
||||||
|
TEST_ASSERT_EQUAL(data_out[TRANSFER_COUNT - 4], tester.get_prev_to_slave_4());
|
||||||
|
TEST_ASSERT_EQUAL(data_out[TRANSFER_COUNT - 3], tester.get_prev_to_slave_3());
|
||||||
|
TEST_ASSERT_EQUAL(data_out[TRANSFER_COUNT - 2], tester.get_prev_to_slave_2());
|
||||||
|
TEST_ASSERT_EQUAL(data_out[TRANSFER_COUNT - 1], tester.get_prev_to_slave_1());
|
||||||
|
TEST_ASSERT_EQUAL(num_writes, tester.num_writes());
|
||||||
|
TEST_ASSERT_EQUAL(num_reads, tester.num_reads());
|
||||||
|
|
||||||
|
tester.reset();
|
||||||
|
tester.pin_set_pull(sda, MbedTester::PullNone);
|
||||||
|
tester.pin_set_pull(scl, MbedTester::PullNone);
|
||||||
|
}
|
||||||
|
|
||||||
|
void i2c_test_byte_read(PinName sda, PinName scl)
|
||||||
|
{
|
||||||
|
// Remap pins for test
|
||||||
|
tester.reset();
|
||||||
|
tester.pin_map_set(sda, MbedTester::LogicalPinI2CSda);
|
||||||
|
tester.pin_map_set(scl, MbedTester::LogicalPinI2CScl);
|
||||||
|
|
||||||
|
tester.pin_set_pull(sda, MbedTester::PullUp);
|
||||||
|
tester.pin_set_pull(scl, MbedTester::PullUp);
|
||||||
|
|
||||||
|
// Initialize mbed I2C pins
|
||||||
|
i2c_t i2c;
|
||||||
|
memset(&i2c, 0, sizeof(i2c));
|
||||||
|
i2c_init(&i2c, sda, scl);
|
||||||
|
i2c_frequency(&i2c, 100000);
|
||||||
|
|
||||||
|
// Reset tester stats and select I2C
|
||||||
|
tester.peripherals_reset();
|
||||||
|
tester.select_peripheral(I2CTester::PeripheralI2C);
|
||||||
|
|
||||||
|
// Data out and in buffers and initialization
|
||||||
|
uint8_t data_out[TRANSFER_COUNT];
|
||||||
|
for (int i = 0; i < TRANSFER_COUNT; i++) {
|
||||||
|
data_out[i] = i & 0xFF;
|
||||||
|
}
|
||||||
|
uint8_t data_in[TRANSFER_COUNT];
|
||||||
|
for (int i = 0; i < TRANSFER_COUNT; i++) {
|
||||||
|
data_in[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int num_writes;
|
||||||
|
int num_reads;
|
||||||
|
int num_acks;
|
||||||
|
int num_nacks;
|
||||||
|
int num_starts;
|
||||||
|
int num_stops;
|
||||||
|
uint32_t checksum;
|
||||||
|
int num_dev_addr_matches;
|
||||||
|
int ack_nack;//0 if NACK was received, 1 if ACK was received, 2 for timeout
|
||||||
|
|
||||||
|
// Reset tester stats and select I2C
|
||||||
|
tester.peripherals_reset();
|
||||||
|
tester.select_peripheral(MbedTester::PeripheralI2C);
|
||||||
|
for (int i = 0; i < TRANSFER_COUNT; i++) {
|
||||||
|
data_in[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read data for I2C single byte transfers
|
||||||
|
// Will read <TRANSFER_COUNT> bytes, checksum must match checksum calculated in parallel on FPGA
|
||||||
|
num_dev_addr_matches = 0;
|
||||||
|
num_writes = 0;
|
||||||
|
num_reads = 0;
|
||||||
|
num_starts = 0;
|
||||||
|
num_stops = 0;
|
||||||
|
num_acks = 0;
|
||||||
|
num_nacks = 0;
|
||||||
|
checksum = 0;
|
||||||
|
|
||||||
|
i2c_start(&i2c);//start condition
|
||||||
|
num_starts += 1;
|
||||||
|
i2c_byte_write(&i2c, (I2C_DEV_ADDR | 1));//send device address for reading
|
||||||
|
num_dev_addr_matches += 1;
|
||||||
|
num_acks += 1;
|
||||||
|
for (int i = 0; i < TRANSFER_COUNT; i++) {
|
||||||
|
if (num_reads == (TRANSFER_COUNT - 1)) {
|
||||||
|
data_in[i] = i2c_byte_read(&i2c, 1);//send NACK
|
||||||
|
checksum += data_in[i];
|
||||||
|
num_reads += 1;
|
||||||
|
num_nacks += 1;
|
||||||
|
} else {
|
||||||
|
data_in[i] = i2c_byte_read(&i2c, 0);//send ACK
|
||||||
|
checksum += data_in[i];
|
||||||
|
num_reads += 1;
|
||||||
|
num_acks += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_stop(&i2c);
|
||||||
|
num_stops += 1;
|
||||||
|
|
||||||
|
// Verify that the transfer was successful
|
||||||
|
TEST_ASSERT_EQUAL(num_dev_addr_matches, tester.num_dev_addr_matches());
|
||||||
|
TEST_ASSERT_EQUAL(TRANSFER_COUNT, num_reads);
|
||||||
|
TEST_ASSERT_EQUAL(num_reads + 1, tester.transfer_count());
|
||||||
|
TEST_ASSERT_EQUAL(num_starts, tester.num_starts());
|
||||||
|
TEST_ASSERT_EQUAL(num_stops, tester.num_stops());
|
||||||
|
TEST_ASSERT_EQUAL(num_acks, tester.num_acks());
|
||||||
|
TEST_ASSERT_EQUAL(num_nacks, tester.num_nacks());
|
||||||
|
TEST_ASSERT_EQUAL(checksum, tester.get_send_checksum());
|
||||||
|
TEST_ASSERT_EQUAL(0, tester.state_num());
|
||||||
|
TEST_ASSERT_EQUAL(((TRANSFER_COUNT + 2) & 0xFF), tester.get_next_from_slave());
|
||||||
|
TEST_ASSERT_EQUAL(num_writes, tester.num_writes());
|
||||||
|
TEST_ASSERT_EQUAL(num_reads, tester.num_reads());
|
||||||
|
|
||||||
|
tester.reset();
|
||||||
|
tester.pin_set_pull(sda, MbedTester::PullNone);
|
||||||
|
tester.pin_set_pull(scl, MbedTester::PullNone);
|
||||||
|
}
|
||||||
|
|
||||||
|
Case cases[] = {
|
||||||
|
Case("i2c - init/free test all pins", all_ports<I2CPort, DefaultFormFactor, test_i2c_init_free>),
|
||||||
|
Case("i2c - test write i2c API", all_peripherals<I2CPort, DefaultFormFactor, i2c_test_write>),
|
||||||
|
Case("i2c - test read i2c API", all_peripherals<I2CPort, DefaultFormFactor, i2c_test_read>),
|
||||||
|
Case("i2c - test single byte write i2c API", all_peripherals<I2CPort, DefaultFormFactor, i2c_test_byte_write>),
|
||||||
|
Case("i2c - test single byte read i2c API", all_peripherals<I2CPort, DefaultFormFactor, i2c_test_byte_read>)
|
||||||
|
};
|
||||||
|
|
||||||
|
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
||||||
|
{
|
||||||
|
GREENTEA_SETUP(15, "default_auto");
|
||||||
|
return greentea_test_setup_handler(number_of_cases);
|
||||||
|
}
|
||||||
|
|
||||||
|
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
Harness::run(specification);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !DEVICE_I2C */
|
|
@ -0,0 +1,226 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !DEVICE_PWMOUT
|
||||||
|
#error [NOT_SUPPORTED] PWM not supported for this target
|
||||||
|
#elif !COMPONENT_FPGA_CI_TEST_SHIELD
|
||||||
|
#error [NOT_SUPPORTED] FPGA CI Test Shield is needed to run this test
|
||||||
|
#elif !defined(TARGET_FF_ARDUINO) && !defined(MBED_CONF_TARGET_DEFAULT_FORM_FACTOR)
|
||||||
|
#error [NOT_SUPPORTED] Test not supported for this form factor
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include "utest/utest.h"
|
||||||
|
#include "unity/unity.h"
|
||||||
|
#include "greentea-client/test_env.h"
|
||||||
|
#include "mbed.h"
|
||||||
|
|
||||||
|
using namespace utest::v1;
|
||||||
|
|
||||||
|
#include "MbedTester.h"
|
||||||
|
#include "pinmap.h"
|
||||||
|
#include "test_utils.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define pwm_debug_printf(...)
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PERIOD_WRITE,
|
||||||
|
PERIOD_MS_WRITE,
|
||||||
|
PERIOD_US_WRITE,
|
||||||
|
PERIOD_PULSEWIDTH,
|
||||||
|
PERIOD_PULSEWIDTH_MS,
|
||||||
|
PERIOD_PULSEWIDTH_US
|
||||||
|
} pwm_api_test_t;
|
||||||
|
|
||||||
|
#define NUM_OF_PERIODS 10
|
||||||
|
#define US_PER_SEC 1000000
|
||||||
|
#define US_PER_MS 1000
|
||||||
|
#define MS_PER_SEC 1000
|
||||||
|
|
||||||
|
#define DELTA_FACTOR 20 // 5% delta
|
||||||
|
|
||||||
|
#define PERIOD_US(PERIOD_MS) (((PERIOD_MS) * US_PER_MS))
|
||||||
|
#define PERIOD_FLOAT(PERIOD_MS) (((float)(PERIOD_MS) / US_PER_MS))
|
||||||
|
#define FILL_FLOAT(PRC) ((float)(PRC) / 100)
|
||||||
|
#define PULSE_HIGH_US(PERIOD_US, PRC) ((uint32_t)((PERIOD_US) * FILL_FLOAT(PRC)))
|
||||||
|
#define PULSE_LOW_US(PERIOD_US, PRC) ((uint32_t)((PERIOD_US) * (1.0f - FILL_FLOAT(PRC))))
|
||||||
|
|
||||||
|
|
||||||
|
MbedTester tester(DefaultFormFactor::pins(), DefaultFormFactor::restricted_pins());
|
||||||
|
|
||||||
|
|
||||||
|
void pwm_init_free(PinName pin)
|
||||||
|
{
|
||||||
|
pwmout_t pwm_out;
|
||||||
|
|
||||||
|
pwm_debug_printf("PWM init/free test on pin=%s (%i)\r\n", pinmap_ff_default_pin_to_string(pin), pin);
|
||||||
|
|
||||||
|
pwmout_init(&pwm_out, pin);
|
||||||
|
pwmout_free(&pwm_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void pwm_period_fill_test(PinName pin, uint32_t period_ms, uint32_t fill_prc, pwm_api_test_t api_test)
|
||||||
|
{
|
||||||
|
pwm_debug_printf("PWM test on pin = %s (%i)\r\n", pinmap_ff_default_pin_to_string(pin), pin);
|
||||||
|
pwm_debug_printf("Testing period = %lu ms, duty-cycle = %lu %%\r\n", period_ms, fill_prc);
|
||||||
|
pwm_debug_printf("Testing APIs = %d\r\n", (int)api_test);
|
||||||
|
|
||||||
|
tester.reset();
|
||||||
|
MbedTester::LogicalPin logical_pin = (MbedTester::LogicalPin)(MbedTester::LogicalPinIOMetrics0);
|
||||||
|
tester.pin_map_set(pin, logical_pin);
|
||||||
|
|
||||||
|
pwmout_t pwm_out;
|
||||||
|
|
||||||
|
pwmout_init(&pwm_out, pin);
|
||||||
|
|
||||||
|
core_util_critical_section_enter();
|
||||||
|
|
||||||
|
switch (api_test) {
|
||||||
|
case PERIOD_WRITE:
|
||||||
|
pwmout_period(&pwm_out, PERIOD_FLOAT(period_ms));
|
||||||
|
pwmout_write(&pwm_out, FILL_FLOAT(fill_prc));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PERIOD_MS_WRITE:
|
||||||
|
pwmout_period_ms(&pwm_out, (int)period_ms);
|
||||||
|
pwmout_write(&pwm_out, FILL_FLOAT(fill_prc));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PERIOD_US_WRITE:
|
||||||
|
pwmout_period_us(&pwm_out, PERIOD_US(period_ms));
|
||||||
|
pwmout_write(&pwm_out, FILL_FLOAT(fill_prc));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PERIOD_PULSEWIDTH:
|
||||||
|
pwmout_period(&pwm_out, PERIOD_FLOAT(period_ms));
|
||||||
|
pwmout_pulsewidth(&pwm_out, (float)PULSE_HIGH_US(PERIOD_US(period_ms), fill_prc) / US_PER_SEC);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PERIOD_PULSEWIDTH_MS:
|
||||||
|
pwmout_period(&pwm_out, PERIOD_FLOAT(period_ms));
|
||||||
|
pwmout_pulsewidth_ms(&pwm_out, (int)PULSE_HIGH_US(PERIOD_US(period_ms), fill_prc) / MS_PER_SEC);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PERIOD_PULSEWIDTH_US:
|
||||||
|
pwmout_period(&pwm_out, PERIOD_FLOAT(period_ms));
|
||||||
|
pwmout_pulsewidth_us(&pwm_out, (int)PULSE_HIGH_US(PERIOD_US(period_ms), fill_prc));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tester.io_metrics_start();
|
||||||
|
|
||||||
|
wait(NUM_OF_PERIODS * PERIOD_FLOAT(period_ms));
|
||||||
|
|
||||||
|
tester.io_metrics_stop();
|
||||||
|
core_util_critical_section_exit();
|
||||||
|
|
||||||
|
const uint32_t expected_low_pulse_us = PULSE_LOW_US(PERIOD_US(period_ms), fill_prc);
|
||||||
|
const uint32_t expected_high_pulse_us = PULSE_HIGH_US(PERIOD_US(period_ms), fill_prc);
|
||||||
|
const uint32_t delta_low_pulse = (expected_low_pulse_us / DELTA_FACTOR);
|
||||||
|
const uint32_t delta_high_pulse = (expected_high_pulse_us / DELTA_FACTOR);
|
||||||
|
|
||||||
|
pwm_debug_printf("Minimum pulse low %lu us\r\n", tester.io_metrics_min_pulse_low(logical_pin) / 100);
|
||||||
|
pwm_debug_printf("Minimum pulse high %lu us\r\n", tester.io_metrics_min_pulse_high(logical_pin) / 100);
|
||||||
|
pwm_debug_printf("Maximum pulse low %lu us\r\n", tester.io_metrics_max_pulse_low(logical_pin) / 100);
|
||||||
|
pwm_debug_printf("Maximum pulse high %lu us\r\n", tester.io_metrics_max_pulse_high(logical_pin) / 100);
|
||||||
|
pwm_debug_printf("Rising edges %lu\r\n", tester.io_metrics_rising_edges(logical_pin));
|
||||||
|
pwm_debug_printf("Falling edges %lu\r\n", tester.io_metrics_falling_edges(logical_pin));
|
||||||
|
|
||||||
|
TEST_ASSERT_FLOAT_WITHIN(FILL_FLOAT(fill_prc) / DELTA_FACTOR, FILL_FLOAT(fill_prc), pwmout_read(&pwm_out));
|
||||||
|
|
||||||
|
TEST_ASSERT_UINT32_WITHIN(delta_low_pulse, expected_low_pulse_us, tester.io_metrics_min_pulse_low(logical_pin) / 100);
|
||||||
|
TEST_ASSERT_UINT32_WITHIN(delta_low_pulse, expected_low_pulse_us, tester.io_metrics_max_pulse_low(logical_pin) / 100);
|
||||||
|
TEST_ASSERT_UINT32_WITHIN(delta_high_pulse, expected_high_pulse_us, tester.io_metrics_min_pulse_high(logical_pin) / 100);
|
||||||
|
TEST_ASSERT_UINT32_WITHIN(delta_high_pulse, expected_high_pulse_us, tester.io_metrics_max_pulse_high(logical_pin) / 100);
|
||||||
|
|
||||||
|
TEST_ASSERT_UINT32_WITHIN(1, NUM_OF_PERIODS, tester.io_metrics_rising_edges(logical_pin));
|
||||||
|
TEST_ASSERT_UINT32_WITHIN(1, NUM_OF_PERIODS, tester.io_metrics_falling_edges(logical_pin));
|
||||||
|
|
||||||
|
pwmout_free(&pwm_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<uint32_t period_ms, uint32_t fill_prc, pwm_api_test_t api_test>
|
||||||
|
void pwm_period_fill_test(PinName pin)
|
||||||
|
{
|
||||||
|
pwm_period_fill_test(pin, period_ms, fill_prc, api_test);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Case cases[] = {
|
||||||
|
// This will be run for all pins
|
||||||
|
Case("PWM - init/free test", all_ports<PWMPort, DefaultFormFactor, pwm_init_free>),
|
||||||
|
|
||||||
|
// This will be run for single pin
|
||||||
|
Case("PWM - period: 10 ms, fill: 10%, api: period/write", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<10, 10, PERIOD_WRITE> >),
|
||||||
|
|
||||||
|
Case("PWM - period: 10 ms, fill: 10%, api: period_ms/write", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<10, 10, PERIOD_MS_WRITE> >),
|
||||||
|
Case("PWM - period: 10 ms, fill: 10%, api: period_us/write", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<10, 10, PERIOD_US_WRITE> >),
|
||||||
|
Case("PWM - period: 10 ms, fill: 10%, api: period/pulse_width", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<10, 10, PERIOD_PULSEWIDTH> >),
|
||||||
|
Case("PWM - period: 10 ms, fill: 10%, api: period/pulse_width_ms", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<10, 10, PERIOD_PULSEWIDTH_MS> >),
|
||||||
|
Case("PWM - period: 10 ms, fill: 10%, api: period/pulse_width_us", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<10, 10, PERIOD_PULSEWIDTH_US> >),
|
||||||
|
|
||||||
|
Case("PWM - period: 10 ms, fill: 50%, api: period/write", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<10, 50, PERIOD_WRITE> >),
|
||||||
|
Case("PWM - period: 10 ms, fill: 50%, api: period_ms/write", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<10, 50, PERIOD_MS_WRITE> >),
|
||||||
|
Case("PWM - period: 10 ms, fill: 50%, api: period_us/write", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<10, 50, PERIOD_US_WRITE> >),
|
||||||
|
Case("PWM - period: 10 ms, fill: 50%, api: period/pulse_width", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<10, 50, PERIOD_PULSEWIDTH> >),
|
||||||
|
Case("PWM - period: 10 ms, fill: 50%, api: period/pulse_width_ms", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<10, 50, PERIOD_PULSEWIDTH_MS> >),
|
||||||
|
Case("PWM - period: 10 ms, fill: 50%, api: period/pulse_width_us", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<10, 50, PERIOD_PULSEWIDTH_US> >),
|
||||||
|
|
||||||
|
Case("PWM - period: 10 ms, fill: 90%, api: period/write", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<10, 90, PERIOD_WRITE> >),
|
||||||
|
Case("PWM - period: 10 ms, fill: 90%, api: period_ms/write", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<10, 90, PERIOD_MS_WRITE> >),
|
||||||
|
Case("PWM - period: 10 ms, fill: 90%, api: period_us/write", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<10, 90, PERIOD_US_WRITE> >),
|
||||||
|
Case("PWM - period: 10 ms, fill: 90%, api: period/pulse_width", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<10, 90, PERIOD_PULSEWIDTH> >),
|
||||||
|
Case("PWM - period: 10 ms, fill: 90%, api: period/pulse_width_ms", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<10, 90, PERIOD_PULSEWIDTH_MS> >),
|
||||||
|
Case("PWM - period: 10 ms, fill: 90%, api: period/pulse_width_us", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<10, 90, PERIOD_PULSEWIDTH_US> >),
|
||||||
|
|
||||||
|
Case("PWM - period: 50 ms, fill: 10%, api: period/write", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<50, 10, PERIOD_WRITE> >),
|
||||||
|
Case("PWM - period: 50 ms, fill: 10%, api: period_ms/write", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<50, 10, PERIOD_MS_WRITE> >),
|
||||||
|
Case("PWM - period: 50 ms, fill: 10%, api: period_us/write", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<50, 10, PERIOD_US_WRITE> >),
|
||||||
|
Case("PWM - period: 50 ms, fill: 10%, api: period/pulse_width", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<50, 10, PERIOD_PULSEWIDTH> >),
|
||||||
|
Case("PWM - period: 50 ms, fill: 10%, api: period/pulse_width_ms", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<50, 10, PERIOD_PULSEWIDTH_MS> >),
|
||||||
|
Case("PWM - period: 50 ms, fill: 10%, api: period/pulse_width_us", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<50, 10, PERIOD_PULSEWIDTH_US> >),
|
||||||
|
|
||||||
|
Case("PWM - period: 50 ms, fill: 50%, api: period/write", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<50, 50, PERIOD_WRITE> >),
|
||||||
|
Case("PWM - period: 50 ms, fill: 50%, api: period_ms/write", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<50, 50, PERIOD_MS_WRITE> >),
|
||||||
|
Case("PWM - period: 50 ms, fill: 50%, api: period_us/write", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<50, 50, PERIOD_US_WRITE> >),
|
||||||
|
Case("PWM - period: 50 ms, fill: 50%, api: period/pulse_width", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<50, 50, PERIOD_PULSEWIDTH> >),
|
||||||
|
Case("PWM - period: 50 ms, fill: 50%, api: period/pulse_width_ms", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<50, 50, PERIOD_PULSEWIDTH_MS> >),
|
||||||
|
Case("PWM - period: 50 ms, fill: 50%, api: period/pulse_width_us", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<50, 50, PERIOD_PULSEWIDTH_US> >),
|
||||||
|
|
||||||
|
Case("PWM - period: 50 ms, fill: 90%, api: period/write", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<50, 90, PERIOD_WRITE> >),
|
||||||
|
Case("PWM - period: 50 ms, fill: 90%, api: period_ms/write", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<50, 90, PERIOD_MS_WRITE> >),
|
||||||
|
Case("PWM - period: 50 ms, fill: 90%, api: period_us/write", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<50, 90, PERIOD_US_WRITE> >),
|
||||||
|
Case("PWM - period: 50 ms, fill: 90%, api: period/pulse_width", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<50, 90, PERIOD_PULSEWIDTH> >),
|
||||||
|
Case("PWM - period: 50 ms, fill: 90%, api: period/pulse_width_ms", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<50, 90, PERIOD_PULSEWIDTH_MS> >),
|
||||||
|
Case("PWM - period: 50 ms, fill: 90%, api: period/pulse_width_us", one_peripheral<PWMPort, DefaultFormFactor, pwm_period_fill_test<50, 90, PERIOD_PULSEWIDTH_US> >)
|
||||||
|
};
|
||||||
|
|
||||||
|
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
||||||
|
{
|
||||||
|
GREENTEA_SETUP(120, "default_auto");
|
||||||
|
return greentea_test_setup_handler(number_of_cases);
|
||||||
|
}
|
||||||
|
|
||||||
|
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
Harness::run(specification);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !DEVICE_PWMOUT */
|
|
@ -0,0 +1,208 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !DEVICE_SPI
|
||||||
|
#error [NOT_SUPPORTED] SPI not supported for this target
|
||||||
|
#elif !COMPONENT_FPGA_CI_TEST_SHIELD
|
||||||
|
#error [NOT_SUPPORTED] FPGA CI Test Shield is needed to run this test
|
||||||
|
#elif !defined(TARGET_FF_ARDUINO) && !defined(MBED_CONF_TARGET_DEFAULT_FORM_FACTOR)
|
||||||
|
#error [NOT_SUPPORTED] Test not supported for this form factor
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include "utest/utest.h"
|
||||||
|
#include "unity/unity.h"
|
||||||
|
#include "greentea-client/test_env.h"
|
||||||
|
#include "mbed.h"
|
||||||
|
|
||||||
|
using namespace utest::v1;
|
||||||
|
|
||||||
|
#include "SPIMasterTester.h"
|
||||||
|
#include "pinmap.h"
|
||||||
|
#include "test_utils.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TRANSFER_SPI_MASTER_WRITE_SYNC,
|
||||||
|
TRANSFER_SPI_MASTER_BLOCK_WRITE_SYNC,
|
||||||
|
TRANSFER_SPI_MASTER_TRANSFER_ASYNC
|
||||||
|
} transfer_type_t;
|
||||||
|
|
||||||
|
#define FREQ_500_KHZ 500000
|
||||||
|
#define FREQ_1_MHZ 1000000
|
||||||
|
#define FREQ_2_MHZ 2000000
|
||||||
|
|
||||||
|
const int TRANSFER_COUNT = 300;
|
||||||
|
SPIMasterTester tester(DefaultFormFactor::pins(), DefaultFormFactor::restricted_pins());
|
||||||
|
|
||||||
|
spi_t spi;
|
||||||
|
static volatile bool async_trasfer_done;
|
||||||
|
|
||||||
|
#if DEVICE_SPI_ASYNCH
|
||||||
|
void spi_async_handler()
|
||||||
|
{
|
||||||
|
int event = spi_irq_handler_asynch(&spi);
|
||||||
|
|
||||||
|
if (event == SPI_EVENT_COMPLETE) {
|
||||||
|
async_trasfer_done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void spi_test_init_free(PinName mosi, PinName miso, PinName sclk, PinName ssel)
|
||||||
|
{
|
||||||
|
spi_init(&spi, mosi, miso, sclk, ssel);
|
||||||
|
spi_format(&spi, 8, SPITester::Mode0, 0);
|
||||||
|
spi_frequency(&spi, 1000000);
|
||||||
|
spi_free(&spi);
|
||||||
|
}
|
||||||
|
|
||||||
|
void spi_test_common(PinName mosi, PinName miso, PinName sclk, PinName ssel, SPITester::SpiMode spi_mode, uint32_t sym_size, transfer_type_t transfer_type, uint32_t frequency)
|
||||||
|
{
|
||||||
|
uint32_t sym_mask = ((1 << sym_size) - 1);
|
||||||
|
|
||||||
|
// Remap pins for test
|
||||||
|
tester.reset();
|
||||||
|
tester.pin_map_set(mosi, MbedTester::LogicalPinSPIMosi);
|
||||||
|
tester.pin_map_set(miso, MbedTester::LogicalPinSPIMiso);
|
||||||
|
tester.pin_map_set(sclk, MbedTester::LogicalPinSPISclk);
|
||||||
|
tester.pin_map_set(ssel, MbedTester::LogicalPinSPISsel);
|
||||||
|
|
||||||
|
// Initialize mbed SPI pins
|
||||||
|
|
||||||
|
spi_init(&spi, mosi, miso, sclk, ssel);
|
||||||
|
spi_format(&spi, sym_size, spi_mode, 0);
|
||||||
|
spi_frequency(&spi, frequency);
|
||||||
|
|
||||||
|
// Configure spi_slave module
|
||||||
|
tester.set_mode(spi_mode);
|
||||||
|
tester.set_bit_order(SPITester::MSBFirst);
|
||||||
|
tester.set_sym_size(sym_size);
|
||||||
|
|
||||||
|
// Reset tester stats and select SPI
|
||||||
|
tester.peripherals_reset();
|
||||||
|
tester.select_peripheral(SPITester::PeripheralSPI);
|
||||||
|
|
||||||
|
uint32_t checksum = 0;
|
||||||
|
int result = 0;
|
||||||
|
uint8_t tx_buf[TRANSFER_COUNT] = {0};
|
||||||
|
uint8_t rx_buf[TRANSFER_COUNT] = {0};
|
||||||
|
|
||||||
|
// Send and receive test data
|
||||||
|
switch (transfer_type) {
|
||||||
|
case TRANSFER_SPI_MASTER_WRITE_SYNC:
|
||||||
|
for (int i = 0; i < TRANSFER_COUNT; i++) {
|
||||||
|
uint32_t data = spi_master_write(&spi, (0 - i) & sym_mask);
|
||||||
|
TEST_ASSERT_EQUAL(i & sym_mask, data);
|
||||||
|
|
||||||
|
checksum += (0 - i) & sym_mask;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TRANSFER_SPI_MASTER_BLOCK_WRITE_SYNC:
|
||||||
|
for (int i = 0; i < TRANSFER_COUNT; i++) {
|
||||||
|
tx_buf[i] = (0 - i) & sym_mask;
|
||||||
|
checksum += (0 - i) & sym_mask;
|
||||||
|
rx_buf[i] = 0xAA;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = spi_master_block_write(&spi, (const char *)tx_buf, TRANSFER_COUNT, (char *)rx_buf, TRANSFER_COUNT, 0xF5);
|
||||||
|
|
||||||
|
for (int i = 0; i < TRANSFER_COUNT; i++) {
|
||||||
|
TEST_ASSERT_EQUAL(i & sym_mask, rx_buf[i]);
|
||||||
|
}
|
||||||
|
TEST_ASSERT_EQUAL(TRANSFER_COUNT, result);
|
||||||
|
break;
|
||||||
|
|
||||||
|
#if DEVICE_SPI_ASYNCH
|
||||||
|
case TRANSFER_SPI_MASTER_TRANSFER_ASYNC:
|
||||||
|
for (int i = 0; i < TRANSFER_COUNT; i++) {
|
||||||
|
tx_buf[i] = (0 - i) & sym_mask;
|
||||||
|
checksum += (0 - i) & sym_mask;
|
||||||
|
rx_buf[i] = 0xAA;
|
||||||
|
}
|
||||||
|
|
||||||
|
async_trasfer_done = false;
|
||||||
|
|
||||||
|
spi_master_transfer(&spi, tx_buf, TRANSFER_COUNT, rx_buf, TRANSFER_COUNT, 8, (uint32_t)spi_async_handler, 0, DMA_USAGE_NEVER);
|
||||||
|
while (!async_trasfer_done);
|
||||||
|
|
||||||
|
for (int i = 0; i < TRANSFER_COUNT; i++) {
|
||||||
|
TEST_ASSERT_EQUAL(i & sym_mask, rx_buf[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
default:
|
||||||
|
TEST_ASSERT_MESSAGE(0, "Unsupported transfer type.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the transfer was successful
|
||||||
|
TEST_ASSERT_EQUAL(TRANSFER_COUNT, tester.get_transfer_count());
|
||||||
|
TEST_ASSERT_EQUAL(checksum, tester.get_receive_checksum());
|
||||||
|
|
||||||
|
spi_free(&spi);
|
||||||
|
tester.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<SPITester::SpiMode spi_mode, uint32_t sym_size, transfer_type_t transfer_type, uint32_t frequency>
|
||||||
|
void spi_test_common(PinName mosi, PinName miso, PinName sclk, PinName ssel)
|
||||||
|
{
|
||||||
|
spi_test_common(mosi, miso, sclk, ssel, spi_mode, sym_size, transfer_type, frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
Case cases[] = {
|
||||||
|
// This will be run for all pins
|
||||||
|
Case("SPI - init/free test all pins", all_ports<SPIPort, DefaultFormFactor, spi_test_init_free>),
|
||||||
|
|
||||||
|
// This will be run for all peripherals
|
||||||
|
Case("SPI - basic test", all_peripherals<SPIPort, DefaultFormFactor, spi_test_common<SPITester::Mode0, 8, TRANSFER_SPI_MASTER_WRITE_SYNC, FREQ_1_MHZ> >),
|
||||||
|
|
||||||
|
// This will be run for single pin configuration
|
||||||
|
Case("SPI - mode testing (MODE_1)", one_peripheral<SPIPort, DefaultFormFactor, spi_test_common<SPITester::Mode1, 8, TRANSFER_SPI_MASTER_WRITE_SYNC, FREQ_1_MHZ> >),
|
||||||
|
Case("SPI - mode testing (MODE_2)", one_peripheral<SPIPort, DefaultFormFactor, spi_test_common<SPITester::Mode2, 8, TRANSFER_SPI_MASTER_WRITE_SYNC, FREQ_1_MHZ> >),
|
||||||
|
Case("SPI - mode testing (MODE_3)", one_peripheral<SPIPort, DefaultFormFactor, spi_test_common<SPITester::Mode3, 8, TRANSFER_SPI_MASTER_WRITE_SYNC, FREQ_1_MHZ> >),
|
||||||
|
|
||||||
|
Case("SPI - symbol size testing (4)", one_peripheral<SPIPort, DefaultFormFactor, spi_test_common<SPITester::Mode0, 4, TRANSFER_SPI_MASTER_WRITE_SYNC, FREQ_1_MHZ> >),
|
||||||
|
Case("SPI - symbol size testing (12)", one_peripheral<SPIPort, DefaultFormFactor, spi_test_common<SPITester::Mode0, 12, TRANSFER_SPI_MASTER_WRITE_SYNC, FREQ_1_MHZ> >),
|
||||||
|
Case("SPI - symbol size testing (16)", one_peripheral<SPIPort, DefaultFormFactor, spi_test_common<SPITester::Mode0, 16, TRANSFER_SPI_MASTER_WRITE_SYNC, FREQ_1_MHZ> >),
|
||||||
|
|
||||||
|
Case("SPI - frequency testing (500 kHz)", one_peripheral<SPIPort, DefaultFormFactor, spi_test_common<SPITester::Mode0, 8, TRANSFER_SPI_MASTER_WRITE_SYNC, FREQ_500_KHZ> >),
|
||||||
|
Case("SPI - frequency testing (2 MHz)", one_peripheral<SPIPort, DefaultFormFactor, spi_test_common<SPITester::Mode0, 8, TRANSFER_SPI_MASTER_WRITE_SYNC, FREQ_2_MHZ> >),
|
||||||
|
|
||||||
|
Case("SPI - block write", one_peripheral<SPIPort, DefaultFormFactor, spi_test_common<SPITester::Mode0, 8, TRANSFER_SPI_MASTER_BLOCK_WRITE_SYNC, FREQ_1_MHZ> >),
|
||||||
|
|
||||||
|
#if DEVICE_SPI_ASYNCH
|
||||||
|
Case("SPI - async mode", one_peripheral<SPIPort, DefaultFormFactor, spi_test_common<SPITester::Mode0, 8, TRANSFER_SPI_MASTER_TRANSFER_ASYNC, FREQ_1_MHZ> >)
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
||||||
|
{
|
||||||
|
GREENTEA_SETUP(60, "default_auto");
|
||||||
|
return greentea_test_setup_handler(number_of_cases);
|
||||||
|
}
|
||||||
|
|
||||||
|
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
Harness::run(specification);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !DEVICE_SPI */
|
|
@ -0,0 +1,343 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !DEVICE_SERIAL
|
||||||
|
#error [NOT_SUPPORTED] SERIAL not supported for this target
|
||||||
|
#elif !COMPONENT_FPGA_CI_TEST_SHIELD
|
||||||
|
#error [NOT_SUPPORTED] FPGA CI Test Shield is needed to run this test
|
||||||
|
#elif !defined(TARGET_FF_ARDUINO) && !defined(MBED_CONF_TARGET_DEFAULT_FORM_FACTOR)
|
||||||
|
#error [NOT_SUPPORTED] Test not supported for this form factor
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include "utest/utest.h"
|
||||||
|
#include "unity/unity.h"
|
||||||
|
#include "greentea-client/test_env.h"
|
||||||
|
|
||||||
|
using namespace utest::v1;
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "UARTTester.h"
|
||||||
|
#include "pinmap.h"
|
||||||
|
#include "test_utils.h"
|
||||||
|
#include "serial_api.h"
|
||||||
|
#include "us_ticker_api.h"
|
||||||
|
|
||||||
|
#define PUTC_REPS 16
|
||||||
|
#define GETC_REPS 16
|
||||||
|
|
||||||
|
// In the UART RX test, the request for the FPGA to start sending data is sent
|
||||||
|
// first. Then the execution is blocked at serial_getc() call. Since the DUT
|
||||||
|
// is not ready to receive UART data instantly after the request, the start of
|
||||||
|
// the actual transmission has to be dalyed.
|
||||||
|
// A measured delay for NUCLEO_F070RB is 193 us.
|
||||||
|
#define TX_START_DELAY_NS 250000
|
||||||
|
|
||||||
|
UARTTester tester(DefaultFormFactor::pins(), DefaultFormFactor::restricted_pins());
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
serial_t *ser;
|
||||||
|
int *rx_buff;
|
||||||
|
uint32_t rx_cnt;
|
||||||
|
int *tx_buff;
|
||||||
|
uint32_t tx_cnt;
|
||||||
|
} serial_test_data_t;
|
||||||
|
|
||||||
|
static void test_irq_handler(uint32_t id, SerialIrq event)
|
||||||
|
{
|
||||||
|
serial_test_data_t *td = (serial_test_data_t *)id;
|
||||||
|
int c = 0x01; // arbitrary, non-zero value
|
||||||
|
if (event == RxIrq) {
|
||||||
|
c = serial_getc(td->ser);
|
||||||
|
core_util_critical_section_enter();
|
||||||
|
if (td->rx_cnt < GETC_REPS) {
|
||||||
|
td->rx_buff[td->rx_cnt] = c;
|
||||||
|
td->rx_cnt++;
|
||||||
|
}
|
||||||
|
core_util_critical_section_exit();
|
||||||
|
} else if (event == TxIrq) {
|
||||||
|
core_util_critical_section_enter();
|
||||||
|
if (td->tx_cnt < PUTC_REPS) {
|
||||||
|
c = td->tx_buff[td->tx_cnt];
|
||||||
|
td->tx_cnt++;
|
||||||
|
}
|
||||||
|
core_util_critical_section_exit();
|
||||||
|
// Send either one of tx_buff[] values or 0x01.
|
||||||
|
serial_putc(td->ser, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uart_test_common(int baudrate, int data_bits, SerialParity parity, int stop_bits, PinName tx, PinName rx, PinName cts = NC, PinName rts = NC)
|
||||||
|
{
|
||||||
|
// The FPGA CI shield only supports None, Odd & Even.
|
||||||
|
// Forced parity is not supported on Atmel, Freescale, Nordic & STM targets.
|
||||||
|
MBED_ASSERT(parity != ParityForced1 && parity != ParityForced0);
|
||||||
|
|
||||||
|
// STM-specific constraints
|
||||||
|
// Only 7, 8 & 9 data bits.
|
||||||
|
MBED_ASSERT(data_bits >= 7 && data_bits <= 9);
|
||||||
|
// Only Odd or Even parity for 7 data bits.
|
||||||
|
if (data_bits == 7) {
|
||||||
|
MBED_ASSERT(parity != ParityNone);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit the actual TX & RX chars to 8 bits for this test.
|
||||||
|
int test_buff_bits = data_bits < 8 ? data_bits : 8;
|
||||||
|
|
||||||
|
// start_bit + data_bits + parity_bit + stop_bits
|
||||||
|
int packet_bits = 1 + data_bits + stop_bits + (parity == ParityNone ? 0 : 1);
|
||||||
|
us_timestamp_t packet_tx_time = 1000000 * packet_bits / baudrate;
|
||||||
|
const ticker_data_t *const us_ticker = get_us_ticker_data();
|
||||||
|
|
||||||
|
bool use_flow_control = (cts != NC && rts != NC) ? true : false;
|
||||||
|
|
||||||
|
// Remap pins for test
|
||||||
|
tester.reset();
|
||||||
|
tester.pin_map_set(tx, MbedTester::LogicalPinUARTRx);
|
||||||
|
tester.pin_map_set(rx, MbedTester::LogicalPinUARTTx);
|
||||||
|
if (use_flow_control) {
|
||||||
|
tester.pin_map_set(cts, MbedTester::LogicalPinUARTRts);
|
||||||
|
tester.pin_map_set(rts, MbedTester::LogicalPinUARTCts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize mbed UART pins
|
||||||
|
serial_t serial;
|
||||||
|
serial_init(&serial, tx, rx);
|
||||||
|
serial_baud(&serial, baudrate);
|
||||||
|
serial_format(&serial, data_bits, parity, stop_bits);
|
||||||
|
if (use_flow_control) {
|
||||||
|
serial_set_flow_control(&serial, FlowControlRTSCTS, rts, cts);
|
||||||
|
} else {
|
||||||
|
serial_set_flow_control(&serial, FlowControlNone, NC, NC);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset tester stats and select UART
|
||||||
|
tester.peripherals_reset();
|
||||||
|
tester.select_peripheral(MbedTester::PeripheralUART);
|
||||||
|
|
||||||
|
// Configure UART module
|
||||||
|
tester.set_baud((uint32_t)baudrate);
|
||||||
|
tester.set_bits((uint8_t)data_bits);
|
||||||
|
tester.set_stops((uint8_t)stop_bits);
|
||||||
|
switch (parity) {
|
||||||
|
case ParityOdd:
|
||||||
|
tester.set_parity(true, true);
|
||||||
|
break;
|
||||||
|
case ParityEven:
|
||||||
|
tester.set_parity(true, false);
|
||||||
|
break;
|
||||||
|
case ParityNone:
|
||||||
|
default:
|
||||||
|
tester.set_parity(false, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (use_flow_control) {
|
||||||
|
tester.cts_deassert_delay(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rx_buff[GETC_REPS] = {};
|
||||||
|
int tx_buff[PUTC_REPS] = {};
|
||||||
|
volatile serial_test_data_t td = {
|
||||||
|
&serial,
|
||||||
|
rx_buff,
|
||||||
|
0,
|
||||||
|
tx_buff,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
uint32_t checksum = 0;
|
||||||
|
|
||||||
|
// DUT TX / FPGA RX
|
||||||
|
int tx_val;
|
||||||
|
tester.rx_start();
|
||||||
|
for (uint32_t reps = 1; reps <= PUTC_REPS; reps++) {
|
||||||
|
tx_val = rand() % (1 << test_buff_bits);
|
||||||
|
checksum += tx_val;
|
||||||
|
serial_putc(&serial, tx_val);
|
||||||
|
us_timestamp_t end_ts = ticker_read_us(us_ticker) + 2 * packet_tx_time;
|
||||||
|
while (tester.rx_get_count() != reps && ticker_read_us(us_ticker) <= end_ts) {
|
||||||
|
// Wait (no longer than twice the time of one packet transfer) for
|
||||||
|
// the FPGA to receive data and update the byte counter.
|
||||||
|
}
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(reps, tester.rx_get_count());
|
||||||
|
TEST_ASSERT_EQUAL(0, tester.rx_get_parity_errors());
|
||||||
|
TEST_ASSERT_EQUAL(0, tester.rx_get_stop_errors());
|
||||||
|
TEST_ASSERT_EQUAL(0, tester.rx_get_framing_errors());
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(checksum, tester.rx_get_checksum());
|
||||||
|
TEST_ASSERT_EQUAL(tx_val, tester.rx_get_data());
|
||||||
|
}
|
||||||
|
tester.rx_stop();
|
||||||
|
|
||||||
|
// DUT RX / FPGA TX
|
||||||
|
// serial_getc() may return 16-bit as well as 8-bit value cast to an int.
|
||||||
|
// Use a random initial value, but make sure it is low enouth,
|
||||||
|
// so the FPGA will not overflow 8 bits when incrementing it.
|
||||||
|
uint16_t tester_buff = rand() % ((1 << test_buff_bits) - GETC_REPS);
|
||||||
|
tester.tx_set_next(tester_buff);
|
||||||
|
tester.tx_set_count(GETC_REPS);
|
||||||
|
if (!use_flow_control) {
|
||||||
|
tester.tx_set_delay(TX_START_DELAY_NS);
|
||||||
|
}
|
||||||
|
tester.tx_start(use_flow_control);
|
||||||
|
for (int i = 0; i < GETC_REPS; i++) {
|
||||||
|
rx_buff[i] = serial_getc(&serial);
|
||||||
|
}
|
||||||
|
tester.tx_stop();
|
||||||
|
for (int i = 0; i < GETC_REPS; tester_buff++, i++) {
|
||||||
|
TEST_ASSERT_EQUAL(tester_buff, rx_buff[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
serial_irq_handler(&serial, test_irq_handler, (uint32_t) &td);
|
||||||
|
|
||||||
|
// DUT TX (IRQ) / FPGA RX
|
||||||
|
tx_val = rand() % ((1 << test_buff_bits) - PUTC_REPS);
|
||||||
|
for (size_t i = 0; i < PUTC_REPS; tx_val++, i++) {
|
||||||
|
td.tx_buff[i] = tx_val;
|
||||||
|
checksum += tx_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
tester.rx_start();
|
||||||
|
core_util_critical_section_enter();
|
||||||
|
td.tx_cnt = 0;
|
||||||
|
// Enable only the TX IRQ.
|
||||||
|
serial_irq_set(&serial, TxIrq, 1);
|
||||||
|
core_util_critical_section_exit();
|
||||||
|
while (core_util_atomic_load_u32(&td.tx_cnt) != PUTC_REPS) {
|
||||||
|
// Wait until the last byte is written to UART TX reg.
|
||||||
|
};
|
||||||
|
core_util_critical_section_enter();
|
||||||
|
serial_irq_set(&serial, TxIrq, 0);
|
||||||
|
core_util_critical_section_exit();
|
||||||
|
us_timestamp_t end_ts = ticker_read_us(us_ticker) + 2 * packet_tx_time;
|
||||||
|
while (ticker_read_us(us_ticker) <= end_ts) {
|
||||||
|
// Wait twice the time of one packet transfer for the FPGA
|
||||||
|
// to receive and process data.
|
||||||
|
};
|
||||||
|
tester.rx_stop();
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(2 * PUTC_REPS, tester.rx_get_count());
|
||||||
|
TEST_ASSERT_EQUAL(0, tester.rx_get_parity_errors());
|
||||||
|
TEST_ASSERT_EQUAL(0, tester.rx_get_stop_errors());
|
||||||
|
TEST_ASSERT_EQUAL(0, tester.rx_get_framing_errors());
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(checksum, tester.rx_get_checksum());
|
||||||
|
TEST_ASSERT_EQUAL(tx_val - 1, tester.rx_get_data());
|
||||||
|
|
||||||
|
// DUT RX (IRQ) / FPGA TX
|
||||||
|
// serial_getc() may return 16-bit as well as 8-bit value cast to an int.
|
||||||
|
// Use a random initial value, but make sure it is low enouth,
|
||||||
|
// so the FPGA will not overflow 8 bits when incrementing it.
|
||||||
|
tester_buff = rand() % ((1 << test_buff_bits) - GETC_REPS);
|
||||||
|
tester.tx_set_next(tester_buff);
|
||||||
|
tester.tx_set_count(GETC_REPS);
|
||||||
|
if (!use_flow_control) {
|
||||||
|
tester.tx_set_delay(TX_START_DELAY_NS);
|
||||||
|
}
|
||||||
|
core_util_critical_section_enter();
|
||||||
|
// Enable only the RX IRQ.
|
||||||
|
serial_irq_set(&serial, RxIrq, 1);
|
||||||
|
core_util_critical_section_exit();
|
||||||
|
tester.rx_start();
|
||||||
|
tester.tx_start(use_flow_control);
|
||||||
|
while (core_util_atomic_load_u32(&td.rx_cnt) != GETC_REPS) {
|
||||||
|
// Wait until the last byte is received to UART RX reg.
|
||||||
|
};
|
||||||
|
core_util_critical_section_enter();
|
||||||
|
serial_irq_set(&serial, RxIrq, 0);
|
||||||
|
core_util_critical_section_exit();
|
||||||
|
tester.tx_stop();
|
||||||
|
tester.rx_stop();
|
||||||
|
for (int i = 0; i < GETC_REPS; tester_buff++, i++) {
|
||||||
|
TEST_ASSERT_EQUAL(tester_buff, td.rx_buff[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure TX IRQ was disabled during the last RX test.
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(checksum, tester.rx_get_checksum());
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(2 * PUTC_REPS, tester.rx_get_count());
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
serial_free(&serial);
|
||||||
|
tester.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_init_free(PinName tx, PinName rx, PinName cts = NC, PinName rts = NC)
|
||||||
|
{
|
||||||
|
bool use_flow_control = (cts != NC && rts != NC) ? true : false;
|
||||||
|
serial_t serial;
|
||||||
|
serial_init(&serial, tx, rx);
|
||||||
|
serial_baud(&serial, 9600);
|
||||||
|
serial_format(&serial, 8, ParityNone, 1);
|
||||||
|
if (use_flow_control) {
|
||||||
|
serial_set_flow_control(&serial, FlowControlRTSCTS, rts, cts);
|
||||||
|
}
|
||||||
|
serial_free(&serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_init_free_no_fc(PinName tx, PinName rx)
|
||||||
|
{
|
||||||
|
test_init_free(tx, rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int BAUDRATE, int DATA_BITS, SerialParity PARITY, int STOP_BITS>
|
||||||
|
void test_common(PinName tx, PinName rx, PinName cts, PinName rts)
|
||||||
|
{
|
||||||
|
uart_test_common(BAUDRATE, DATA_BITS, PARITY, STOP_BITS, tx, rx, cts, rts);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int BAUDRATE, int DATA_BITS, SerialParity PARITY, int STOP_BITS>
|
||||||
|
void test_common_no_fc(PinName tx, PinName rx)
|
||||||
|
{
|
||||||
|
uart_test_common(BAUDRATE, DATA_BITS, PARITY, STOP_BITS, tx, rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
Case cases[] = {
|
||||||
|
// Every set of pins from every peripheral.
|
||||||
|
Case("init/free, FC on", all_ports<UARTPort, DefaultFormFactor, test_init_free>),
|
||||||
|
Case("init/free, FC off", all_ports<UARTNoFCPort, DefaultFormFactor, test_init_free_no_fc>),
|
||||||
|
|
||||||
|
// One set of pins from every peripheral.
|
||||||
|
Case("basic, 9600, 8N1, FC on", all_peripherals<UARTPort, DefaultFormFactor, test_common<9600, 8, ParityNone, 1> >),
|
||||||
|
Case("basic, 9600, 8N1, FC off", all_peripherals<UARTNoFCPort, DefaultFormFactor, test_common_no_fc<9600, 8, ParityNone, 1> >),
|
||||||
|
|
||||||
|
// One set of pins from one peripheral.
|
||||||
|
// baudrate
|
||||||
|
Case("19200, 8N1, FC on", one_peripheral<UARTPort, DefaultFormFactor, test_common<19200, 8, ParityNone, 1> >),
|
||||||
|
Case("19200, 8N1, FC off", one_peripheral<UARTNoFCPort, DefaultFormFactor, test_common_no_fc<19200, 8, ParityNone, 1> >),
|
||||||
|
Case("38400, 8N1, FC on", one_peripheral<UARTPort, DefaultFormFactor, test_common<38400, 8, ParityNone, 1> >),
|
||||||
|
Case("38400, 8N1, FC off", one_peripheral<UARTNoFCPort, DefaultFormFactor, test_common_no_fc<38400, 8, ParityNone, 1> >),
|
||||||
|
Case("115200, 8N1, FC on", one_peripheral<UARTPort, DefaultFormFactor, test_common<115200, 8, ParityNone, 1> >),
|
||||||
|
Case("115200, 8N1, FC off", one_peripheral<UARTNoFCPort, DefaultFormFactor, test_common_no_fc<115200, 8, ParityNone, 1> >),
|
||||||
|
// data bits: not tested (some platforms support 8 bits only)
|
||||||
|
// parity
|
||||||
|
Case("9600, 8O1, FC on", one_peripheral<UARTPort, DefaultFormFactor, test_common<9600, 8, ParityOdd, 1> >),
|
||||||
|
Case("9600, 8E1, FC on", one_peripheral<UARTPort, DefaultFormFactor, test_common<9600, 8, ParityEven, 1> >),
|
||||||
|
// stop bits
|
||||||
|
Case("9600, 8N2, FC on", one_peripheral<UARTPort, DefaultFormFactor, test_common<9600, 8, ParityNone, 2> >),
|
||||||
|
Case("9600, 8N2, FC off", one_peripheral<UARTNoFCPort, DefaultFormFactor, test_common_no_fc<9600, 8, ParityNone, 2> >),
|
||||||
|
};
|
||||||
|
|
||||||
|
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
||||||
|
{
|
||||||
|
GREENTEA_SETUP(240, "default_auto");
|
||||||
|
srand((unsigned) ticker_read_us(get_us_ticker_data()));
|
||||||
|
return greentea_test_setup_handler(number_of_cases);
|
||||||
|
}
|
||||||
|
|
||||||
|
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
Harness::run(specification);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !DEVICE_SERIAL */
|
|
@ -24,7 +24,7 @@
|
||||||
#include "hal/lp_ticker_api.h"
|
#include "hal/lp_ticker_api.h"
|
||||||
|
|
||||||
//FastModels not support timing test
|
//FastModels not support timing test
|
||||||
#if defined(__ARM_FM)
|
#if defined(TARGET_ARM_FM)
|
||||||
#error [NOT_SUPPORTED] test not supported
|
#error [NOT_SUPPORTED] test not supported
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,10 @@
|
||||||
#include "utest/utest.h"
|
#include "utest/utest.h"
|
||||||
#include "unity/unity.h"
|
#include "unity/unity.h"
|
||||||
|
|
||||||
|
#if defined(SKIP_TIME_DRIFT_TESTS)
|
||||||
|
#error [NOT_SUPPORTED] test not supported
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(MBED_RTOS_SINGLE_THREAD)
|
#if defined(MBED_RTOS_SINGLE_THREAD)
|
||||||
#error [NOT_SUPPORTED] test not supported
|
#error [NOT_SUPPORTED] test not supported
|
||||||
#endif
|
#endif
|
||||||
|
@ -27,11 +31,6 @@
|
||||||
#error [NOT_SUPPORTED] test not supported
|
#error [NOT_SUPPORTED] test not supported
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//FastModels not support time drifting test
|
|
||||||
#if defined(__ARM_FM)
|
|
||||||
#error [NOT_SUPPORTED] test not supported
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using utest::v1::Case;
|
using utest::v1::Case;
|
||||||
|
|
||||||
#if defined(__CORTEX_M23) || defined(__CORTEX_M33)
|
#if defined(__CORTEX_M23) || defined(__CORTEX_M33)
|
||||||
|
|
|
@ -107,7 +107,7 @@ Case cases[] = {
|
||||||
|
|
||||||
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
||||||
{
|
{
|
||||||
GREENTEA_SETUP(10, "timing_drift_auto");
|
GREENTEA_SETUP(10, "default_auto");
|
||||||
return utest::v1::greentea_test_setup_handler(number_of_cases);
|
return utest::v1::greentea_test_setup_handler(number_of_cases);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ using namespace utest::v1;
|
||||||
|
|
||||||
#if defined(__CORTEX_M23) || defined(__CORTEX_M33)
|
#if defined(__CORTEX_M23) || defined(__CORTEX_M33)
|
||||||
#define THREAD_STACK_SIZE 512
|
#define THREAD_STACK_SIZE 512
|
||||||
#elif defined(__ARM_FM)
|
#elif defined(TARGET_ARM_FM)
|
||||||
#define THREAD_STACK_SIZE 512
|
#define THREAD_STACK_SIZE 512
|
||||||
#elif defined(TARGET_CY8CKIT_062_WIFI_BT_PSA)
|
#elif defined(TARGET_CY8CKIT_062_WIFI_BT_PSA)
|
||||||
#define THREAD_STACK_SIZE 512
|
#define THREAD_STACK_SIZE 512
|
||||||
|
|
|
@ -39,7 +39,7 @@ volatile bool thread_should_continue = true;
|
||||||
#define THREAD_STACK_SIZE 512
|
#define THREAD_STACK_SIZE 512
|
||||||
#elif defined(__CORTEX_M23) || defined(__CORTEX_M33)
|
#elif defined(__CORTEX_M23) || defined(__CORTEX_M33)
|
||||||
#define THREAD_STACK_SIZE 512
|
#define THREAD_STACK_SIZE 512
|
||||||
#elif defined(__ARM_FM)
|
#elif defined(TARGET_ARM_FM)
|
||||||
#define THREAD_STACK_SIZE 512
|
#define THREAD_STACK_SIZE 512
|
||||||
#elif defined(TARGET_CY8CKIT_062_WIFI_BT_PSA)
|
#elif defined(TARGET_CY8CKIT_062_WIFI_BT_PSA)
|
||||||
#define THREAD_STACK_SIZE 512
|
#define THREAD_STACK_SIZE 512
|
||||||
|
@ -122,7 +122,7 @@ void test_alloc_and_free(void)
|
||||||
int size = SIZE_INCREMENTS;
|
int size = SIZE_INCREMENTS;
|
||||||
int loop = ALLOC_LOOP;
|
int loop = ALLOC_LOOP;
|
||||||
while (loop) {
|
while (loop) {
|
||||||
data = malloc(size);
|
data = count < ALLOC_ARRAY_SIZE ? malloc(size) : NULL;
|
||||||
if (NULL != data) {
|
if (NULL != data) {
|
||||||
array[count++] = data;
|
array[count++] = data;
|
||||||
memset((void *)data, 0xdeadbeef, size);
|
memset((void *)data, 0xdeadbeef, size);
|
||||||
|
@ -211,7 +211,7 @@ Case cases[] = {
|
||||||
|
|
||||||
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
||||||
{
|
{
|
||||||
GREENTEA_SETUP(test_timeout, "timing_drift_auto");
|
GREENTEA_SETUP(test_timeout, "default_auto");
|
||||||
return utest::v1::greentea_test_setup_handler(number_of_cases);
|
return utest::v1::greentea_test_setup_handler(number_of_cases);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,14 @@ extern "C" {
|
||||||
#define TEST_TICKS 42UL
|
#define TEST_TICKS 42UL
|
||||||
#define DELAY_DELTA_US 2500ULL
|
#define DELAY_DELTA_US 2500ULL
|
||||||
|
|
||||||
|
/* Use a specific delta value for deep sleep, as entry/exit adds up extra latency.
|
||||||
|
* Use deep sleep latency if defined and add 1ms extra delta */
|
||||||
|
#if defined MBED_CONF_TARGET_DEEP_SLEEP_LATENCY
|
||||||
|
#define DEEP_SLEEP_DELAY_DELTA_US ((MBED_CONF_TARGET_DEEP_SLEEP_LATENCY * 1000ULL) + 1000ULL)
|
||||||
|
#else
|
||||||
|
#define DEEP_SLEEP_DELAY_DELTA_US 2500ULL
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace utest::v1;
|
using namespace utest::v1;
|
||||||
|
|
||||||
const us_timestamp_t DELAY_US = 1000000ULL * TEST_TICKS / OS_TICK_FREQ;
|
const us_timestamp_t DELAY_US = 1000000ULL * TEST_TICKS / OS_TICK_FREQ;
|
||||||
|
@ -66,6 +74,11 @@ public:
|
||||||
{
|
{
|
||||||
return _sem.try_acquire_for(millisec);
|
return _sem.try_acquire_for(millisec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sem_acquire()
|
||||||
|
{
|
||||||
|
_sem.acquire();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
timestamp_t mock_ticker_timestamp;
|
timestamp_t mock_ticker_timestamp;
|
||||||
|
@ -275,9 +288,8 @@ void test_sleep(void)
|
||||||
st.schedule_tick(TEST_TICKS);
|
st.schedule_tick(TEST_TICKS);
|
||||||
|
|
||||||
TEST_ASSERT_FALSE_MESSAGE(sleep_manager_can_deep_sleep(), "Deep sleep should be disallowed");
|
TEST_ASSERT_FALSE_MESSAGE(sleep_manager_can_deep_sleep(), "Deep sleep should be disallowed");
|
||||||
while (!st.sem_try_acquire(0)) {
|
st.sem_acquire();
|
||||||
sleep();
|
|
||||||
}
|
|
||||||
timer.stop();
|
timer.stop();
|
||||||
sleep_manager_unlock_deep_sleep();
|
sleep_manager_unlock_deep_sleep();
|
||||||
|
|
||||||
|
@ -305,7 +317,6 @@ void test_deepsleep(void)
|
||||||
* so we'll use the wait_ms() function for now.
|
* so we'll use the wait_ms() function for now.
|
||||||
*/
|
*/
|
||||||
wait_ms(10);
|
wait_ms(10);
|
||||||
|
|
||||||
// Regular Timer might be disabled during deepsleep.
|
// Regular Timer might be disabled during deepsleep.
|
||||||
LowPowerTimer lptimer;
|
LowPowerTimer lptimer;
|
||||||
SysTimerTest st;
|
SysTimerTest st;
|
||||||
|
@ -313,12 +324,10 @@ void test_deepsleep(void)
|
||||||
lptimer.start();
|
lptimer.start();
|
||||||
st.schedule_tick(TEST_TICKS);
|
st.schedule_tick(TEST_TICKS);
|
||||||
TEST_ASSERT_TRUE_MESSAGE(sleep_manager_can_deep_sleep_test_check(), "Deep sleep should be allowed");
|
TEST_ASSERT_TRUE_MESSAGE(sleep_manager_can_deep_sleep_test_check(), "Deep sleep should be allowed");
|
||||||
while (!st.sem_try_acquire(0)) {
|
st.sem_acquire();
|
||||||
sleep();
|
|
||||||
}
|
|
||||||
lptimer.stop();
|
lptimer.stop();
|
||||||
|
|
||||||
TEST_ASSERT_UINT64_WITHIN(DELAY_DELTA_US, DELAY_US, lptimer.read_high_resolution_us());
|
TEST_ASSERT_UINT64_WITHIN(DEEP_SLEEP_DELAY_DELTA_US, DELAY_US, lptimer.read_high_resolution_us());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
#define PARALLEL_THREAD_STACK_SIZE 512
|
#define PARALLEL_THREAD_STACK_SIZE 512
|
||||||
#elif defined(__CORTEX_M23) || defined(__CORTEX_M33)
|
#elif defined(__CORTEX_M23) || defined(__CORTEX_M33)
|
||||||
#define PARALLEL_THREAD_STACK_SIZE 512
|
#define PARALLEL_THREAD_STACK_SIZE 512
|
||||||
#elif defined(__ARM_FM)
|
#elif defined(TARGET_ARM_FM)
|
||||||
#define PARALLEL_THREAD_STACK_SIZE 512
|
#define PARALLEL_THREAD_STACK_SIZE 512
|
||||||
#elif defined(TARGET_CY8CKIT_062_WIFI_BT_PSA)
|
#elif defined(TARGET_CY8CKIT_062_WIFI_BT_PSA)
|
||||||
#define PARALLEL_THREAD_STACK_SIZE 512
|
#define PARALLEL_THREAD_STACK_SIZE 512
|
||||||
|
|
|
@ -20,9 +20,6 @@
|
||||||
(MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE == WIFI && !defined(MBED_CONF_NSAPI_DEFAULT_WIFI_SSID))
|
(MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE == WIFI && !defined(MBED_CONF_NSAPI_DEFAULT_WIFI_SSID))
|
||||||
#error [NOT_SUPPORTED] No network configuration found for this target.
|
#error [NOT_SUPPORTED] No network configuration found for this target.
|
||||||
#endif
|
#endif
|
||||||
#ifndef MBED_CONF_APP_ECHO_SERVER_ADDR
|
|
||||||
#error [NOT_SUPPORTED] Requires parameters from mbed_app.json
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "mbed.h"
|
#include "mbed.h"
|
||||||
#include "greentea-client/test_env.h"
|
#include "greentea-client/test_env.h"
|
||||||
|
@ -31,6 +28,10 @@
|
||||||
#include "utest/utest_stack_trace.h"
|
#include "utest/utest_stack_trace.h"
|
||||||
#include "tcp_tests.h"
|
#include "tcp_tests.h"
|
||||||
|
|
||||||
|
#ifndef ECHO_SERVER_ADDR
|
||||||
|
#error [NOT_SUPPORTED] Requires parameters for echo server
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace utest::v1;
|
using namespace utest::v1;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -84,7 +85,7 @@ nsapi_error_t tcpsocket_connect_to_srv(TCPSocket &sock, uint16_t port)
|
||||||
{
|
{
|
||||||
SocketAddress tcp_addr;
|
SocketAddress tcp_addr;
|
||||||
|
|
||||||
NetworkInterface::get_default_instance()->gethostbyname(MBED_CONF_APP_ECHO_SERVER_ADDR, &tcp_addr);
|
NetworkInterface::get_default_instance()->gethostbyname(ECHO_SERVER_ADDR, &tcp_addr);
|
||||||
tcp_addr.set_port(port);
|
tcp_addr.set_port(port);
|
||||||
|
|
||||||
printf("MBED: Server '%s', port %d\n", tcp_addr.get_ip_address(), tcp_addr.get_port());
|
printf("MBED: Server '%s', port %d\n", tcp_addr.get_ip_address(), tcp_addr.get_port());
|
||||||
|
@ -106,12 +107,12 @@ nsapi_error_t tcpsocket_connect_to_srv(TCPSocket &sock, uint16_t port)
|
||||||
|
|
||||||
nsapi_error_t tcpsocket_connect_to_echo_srv(TCPSocket &sock)
|
nsapi_error_t tcpsocket_connect_to_echo_srv(TCPSocket &sock)
|
||||||
{
|
{
|
||||||
return tcpsocket_connect_to_srv(sock, MBED_CONF_APP_ECHO_SERVER_PORT);
|
return tcpsocket_connect_to_srv(sock, ECHO_SERVER_PORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsapi_error_t tcpsocket_connect_to_discard_srv(TCPSocket &sock)
|
nsapi_error_t tcpsocket_connect_to_discard_srv(TCPSocket &sock)
|
||||||
{
|
{
|
||||||
return tcpsocket_connect_to_srv(sock, MBED_CONF_APP_ECHO_SERVER_DISCARD_PORT);
|
return tcpsocket_connect_to_srv(sock, ECHO_SERVER_DISCARD_PORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_tcp_supported()
|
bool is_tcp_supported()
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
#ifndef TCP_TESTS_H
|
#ifndef TCP_TESTS_H
|
||||||
#define TCP_TESTS_H
|
#define TCP_TESTS_H
|
||||||
|
|
||||||
|
#include "../test_params.h"
|
||||||
|
|
||||||
NetworkInterface *get_interface();
|
NetworkInterface *get_interface();
|
||||||
void drop_bad_packets(TCPSocket &sock, int orig_timeout);
|
void drop_bad_packets(TCPSocket &sock, int orig_timeout);
|
||||||
nsapi_version_t get_ip_version();
|
nsapi_version_t get_ip_version();
|
||||||
|
|
|
@ -33,7 +33,7 @@ void TCPSOCKET_CONNECT_INVALID()
|
||||||
TEST_ASSERT(sock.connect(NULL, 9) < 0);
|
TEST_ASSERT(sock.connect(NULL, 9) < 0);
|
||||||
TEST_ASSERT(sock.connect("", 9) < 0);
|
TEST_ASSERT(sock.connect("", 9) < 0);
|
||||||
TEST_ASSERT(sock.connect("", 0) < 0);
|
TEST_ASSERT(sock.connect("", 0) < 0);
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.connect(MBED_CONF_APP_ECHO_SERVER_ADDR, 9));
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.connect(ECHO_SERVER_ADDR, ECHO_SERVER_DISCARD_PORT));
|
||||||
|
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close());
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close());
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ static nsapi_error_t _tcpsocket_connect_to_daytime_srv(TCPSocket &sock)
|
||||||
{
|
{
|
||||||
SocketAddress tcp_addr;
|
SocketAddress tcp_addr;
|
||||||
|
|
||||||
NetworkInterface::get_default_instance()->gethostbyname(MBED_CONF_APP_ECHO_SERVER_ADDR, &tcp_addr);
|
NetworkInterface::get_default_instance()->gethostbyname(ECHO_SERVER_ADDR, &tcp_addr);
|
||||||
tcp_addr.set_port(13);
|
tcp_addr.set_port(13);
|
||||||
|
|
||||||
nsapi_error_t err = sock.open(NetworkInterface::get_default_instance());
|
nsapi_error_t err = sock.open(NetworkInterface::get_default_instance());
|
||||||
|
|
|
@ -33,7 +33,7 @@ static nsapi_error_t _tcpsocket_connect_to_chargen_srv(TCPSocket &sock)
|
||||||
{
|
{
|
||||||
SocketAddress tcp_addr;
|
SocketAddress tcp_addr;
|
||||||
|
|
||||||
NetworkInterface::get_default_instance()->gethostbyname(MBED_CONF_APP_ECHO_SERVER_ADDR, &tcp_addr);
|
NetworkInterface::get_default_instance()->gethostbyname(ECHO_SERVER_ADDR, &tcp_addr);
|
||||||
tcp_addr.set_port(19);
|
tcp_addr.set_port(19);
|
||||||
|
|
||||||
nsapi_error_t err = sock.open(NetworkInterface::get_default_instance());
|
nsapi_error_t err = sock.open(NetworkInterface::get_default_instance());
|
||||||
|
|
|
@ -40,7 +40,7 @@ void TCPSOCKET_SETSOCKOPT_KEEPALIVE_VALID()
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, ret);
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, ret);
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.connect(MBED_CONF_APP_ECHO_SERVER_ADDR, 9));
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.connect(ECHO_SERVER_ADDR, 9));
|
||||||
// LWIP stack does not support getsockopt so the part below is commented out
|
// LWIP stack does not support getsockopt so the part below is commented out
|
||||||
// int32_t optval;
|
// int32_t optval;
|
||||||
// unsigned int optlen;
|
// unsigned int optlen;
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, ARM Limited, All Rights Reserved
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TEST_PARAMS_H
|
||||||
|
#define TEST_PARAMS_H
|
||||||
|
|
||||||
|
#ifndef MBED_CONF_APP_ECHO_SERVER_ADDR
|
||||||
|
#define ECHO_SERVER_ADDR "echo.mbedcloudtesting.com"
|
||||||
|
#else
|
||||||
|
#define ECHO_SERVER_ADDR MBED_CONF_APP_ECHO_SERVER_ADDR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MBED_CONF_APP_ECHO_SERVER_PORT
|
||||||
|
#define ECHO_SERVER_PORT 7
|
||||||
|
#else
|
||||||
|
#define ECHO_SERVER_PORT MBED_CONF_APP_ECHO_SERVER_PORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MBED_CONF_APP_ECHO_SERVER_DISCARD_PORT
|
||||||
|
#define ECHO_SERVER_DISCARD_PORT 9
|
||||||
|
#else
|
||||||
|
#define ECHO_SERVER_DISCARD_PORT MBED_CONF_APP_ECHO_SERVER_DISCARD_PORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MBED_CONF_APP_ECHO_SERVER_PORT_TLS
|
||||||
|
#define ECHO_SERVER_PORT_TLS 2007
|
||||||
|
#else
|
||||||
|
#define ECHO_SERVER_PORT_TLS MBED_CONF_APP_ECHO_SERVER_PORT_TLS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MBED_CONF_APP_ECHO_SERVER_DISCARD_PORT_TLS
|
||||||
|
#define ECHO_SERVER_DISCARD_PORT_TLS 2009
|
||||||
|
#else
|
||||||
|
#define ECHO_SERVER_DISCARD_PORT_TLS MBED_CONF_APP_ECHO_SERVER_DISCARD_PORT_TLS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //TEST_PARAMS_H
|
|
@ -20,9 +20,6 @@
|
||||||
(MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE == WIFI && !defined(MBED_CONF_NSAPI_DEFAULT_WIFI_SSID))
|
(MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE == WIFI && !defined(MBED_CONF_NSAPI_DEFAULT_WIFI_SSID))
|
||||||
#error [NOT_SUPPORTED] No network configuration found for this target.
|
#error [NOT_SUPPORTED] No network configuration found for this target.
|
||||||
#endif
|
#endif
|
||||||
#ifndef MBED_CONF_APP_ECHO_SERVER_ADDR
|
|
||||||
#error [NOT_SUPPORTED] Requires echo-server-discard-port parameter from mbed_app.json
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "mbed.h"
|
#include "mbed.h"
|
||||||
#include "greentea-client/test_env.h"
|
#include "greentea-client/test_env.h"
|
||||||
|
@ -31,6 +28,10 @@
|
||||||
#include "utest/utest_stack_trace.h"
|
#include "utest/utest_stack_trace.h"
|
||||||
#include "tls_tests.h"
|
#include "tls_tests.h"
|
||||||
|
|
||||||
|
#ifndef ECHO_SERVER_ADDR
|
||||||
|
#error [NOT_SUPPORTED] Requires parameters for echo server
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(MBEDTLS_SSL_CLI_C) || defined(DOXYGEN_ONLY)
|
#if defined(MBEDTLS_SSL_CLI_C) || defined(DOXYGEN_ONLY)
|
||||||
|
|
||||||
using namespace utest::v1;
|
using namespace utest::v1;
|
||||||
|
@ -106,7 +107,7 @@ nsapi_error_t tlssocket_connect_to_srv(TLSSocket &sock, uint16_t port)
|
||||||
{
|
{
|
||||||
SocketAddress tls_addr;
|
SocketAddress tls_addr;
|
||||||
|
|
||||||
NetworkInterface::get_default_instance()->gethostbyname(MBED_CONF_APP_ECHO_SERVER_ADDR, &tls_addr);
|
NetworkInterface::get_default_instance()->gethostbyname(ECHO_SERVER_ADDR, &tls_addr);
|
||||||
tls_addr.set_port(port);
|
tls_addr.set_port(port);
|
||||||
|
|
||||||
printf("MBED: Server '%s', port %d\n", tls_addr.get_ip_address(), tls_addr.get_port());
|
printf("MBED: Server '%s', port %d\n", tls_addr.get_ip_address(), tls_addr.get_port());
|
||||||
|
@ -134,12 +135,12 @@ nsapi_error_t tlssocket_connect_to_srv(TLSSocket &sock, uint16_t port)
|
||||||
|
|
||||||
nsapi_error_t tlssocket_connect_to_echo_srv(TLSSocket &sock)
|
nsapi_error_t tlssocket_connect_to_echo_srv(TLSSocket &sock)
|
||||||
{
|
{
|
||||||
return tlssocket_connect_to_srv(sock, MBED_CONF_APP_ECHO_SERVER_PORT_TLS);
|
return tlssocket_connect_to_srv(sock, ECHO_SERVER_PORT_TLS);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsapi_error_t tlssocket_connect_to_discard_srv(TLSSocket &sock)
|
nsapi_error_t tlssocket_connect_to_discard_srv(TLSSocket &sock)
|
||||||
{
|
{
|
||||||
return tlssocket_connect_to_srv(sock, MBED_CONF_APP_ECHO_SERVER_DISCARD_PORT_TLS);
|
return tlssocket_connect_to_srv(sock, ECHO_SERVER_DISCARD_PORT_TLS);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_tcp_supported()
|
bool is_tcp_supported()
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#ifndef TLS_TESTS_H
|
#ifndef TLS_TESTS_H
|
||||||
#define TLS_TESTS_H
|
#define TLS_TESTS_H
|
||||||
|
|
||||||
|
#include "../test_params.h"
|
||||||
#include "TLSSocket.h"
|
#include "TLSSocket.h"
|
||||||
|
|
||||||
#if defined(MBEDTLS_SSL_CLI_C) || defined(DOXYGEN_ONLY)
|
#if defined(MBEDTLS_SSL_CLI_C) || defined(DOXYGEN_ONLY)
|
||||||
|
|
|
@ -33,12 +33,12 @@ void TLSSOCKET_CONNECT_INVALID()
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.open(NetworkInterface::get_default_instance()));
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.open(NetworkInterface::get_default_instance()));
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.set_root_ca_cert(tls_global::cert));
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.set_root_ca_cert(tls_global::cert));
|
||||||
|
|
||||||
TEST_ASSERT(sock.connect(NULL, MBED_CONF_APP_ECHO_SERVER_DISCARD_PORT_TLS) < 0);
|
TEST_ASSERT(sock.connect(NULL, ECHO_SERVER_DISCARD_PORT_TLS) < 0);
|
||||||
TEST_ASSERT(sock.connect("", MBED_CONF_APP_ECHO_SERVER_DISCARD_PORT_TLS) < 0);
|
TEST_ASSERT(sock.connect("", ECHO_SERVER_DISCARD_PORT_TLS) < 0);
|
||||||
TEST_ASSERT(sock.connect("", 0) < 0);
|
TEST_ASSERT(sock.connect("", 0) < 0);
|
||||||
|
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK,
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK,
|
||||||
sock.connect(MBED_CONF_APP_ECHO_SERVER_ADDR, MBED_CONF_APP_ECHO_SERVER_DISCARD_PORT_TLS));
|
sock.connect(ECHO_SERVER_ADDR, ECHO_SERVER_DISCARD_PORT_TLS));
|
||||||
|
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close());
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close());
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ static nsapi_error_t _tlssocket_connect_to_daytime_srv(TLSSocket &sock)
|
||||||
{
|
{
|
||||||
SocketAddress tls_addr;
|
SocketAddress tls_addr;
|
||||||
|
|
||||||
NetworkInterface::get_default_instance()->gethostbyname(MBED_CONF_APP_ECHO_SERVER_ADDR, &tls_addr);
|
NetworkInterface::get_default_instance()->gethostbyname(ECHO_SERVER_ADDR, &tls_addr);
|
||||||
tls_addr.set_port(2013);
|
tls_addr.set_port(2013);
|
||||||
|
|
||||||
nsapi_error_t err = sock.open(NetworkInterface::get_default_instance());
|
nsapi_error_t err = sock.open(NetworkInterface::get_default_instance());
|
||||||
|
|
|
@ -32,7 +32,7 @@ void TLSSOCKET_NO_CERT()
|
||||||
TLSSocket sock;
|
TLSSocket sock;
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.open(NetworkInterface::get_default_instance()));
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.open(NetworkInterface::get_default_instance()));
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_AUTH_FAILURE,
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_AUTH_FAILURE,
|
||||||
sock.connect(MBED_CONF_APP_ECHO_SERVER_ADDR, MBED_CONF_APP_ECHO_SERVER_PORT_TLS));
|
sock.connect(ECHO_SERVER_ADDR, ECHO_SERVER_PORT_TLS));
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close());
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ void TLSSOCKET_SEND_CLOSED()
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.open(NetworkInterface::get_default_instance()));
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.open(NetworkInterface::get_default_instance()));
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.set_root_ca_cert(tls_global::cert));
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.set_root_ca_cert(tls_global::cert));
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK,
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK,
|
||||||
sock.connect(MBED_CONF_APP_ECHO_SERVER_ADDR, MBED_CONF_APP_ECHO_SERVER_PORT_TLS));
|
sock.connect(ECHO_SERVER_ADDR, ECHO_SERVER_PORT_TLS));
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close());
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close());
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_NO_SOCKET, sock.send("12345", 5));
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_NO_SOCKET, sock.send("12345", 5));
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,6 @@
|
||||||
(MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE == WIFI && !defined(MBED_CONF_NSAPI_DEFAULT_WIFI_SSID))
|
(MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE == WIFI && !defined(MBED_CONF_NSAPI_DEFAULT_WIFI_SSID))
|
||||||
#error [NOT_SUPPORTED] No network configuration found for this target.
|
#error [NOT_SUPPORTED] No network configuration found for this target.
|
||||||
#endif
|
#endif
|
||||||
#ifndef MBED_CONF_APP_ECHO_SERVER_ADDR
|
|
||||||
#error [NOT_SUPPORTED] Requires parameters from mbed_app.json
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "mbed.h"
|
#include "mbed.h"
|
||||||
#include "greentea-client/test_env.h"
|
#include "greentea-client/test_env.h"
|
||||||
|
@ -31,6 +28,10 @@
|
||||||
#include "utest/utest_stack_trace.h"
|
#include "utest/utest_stack_trace.h"
|
||||||
#include "udp_tests.h"
|
#include "udp_tests.h"
|
||||||
|
|
||||||
|
#ifndef ECHO_SERVER_ADDR
|
||||||
|
#error [NOT_SUPPORTED] Requires parameters for echo server
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace utest::v1;
|
using namespace utest::v1;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
#ifndef UDP_TESTS_H
|
#ifndef UDP_TESTS_H
|
||||||
#define UDP_TESTS_H
|
#define UDP_TESTS_H
|
||||||
|
|
||||||
|
#include "../test_params.h"
|
||||||
|
|
||||||
NetworkInterface *get_interface();
|
NetworkInterface *get_interface();
|
||||||
void drop_bad_packets(UDPSocket &sock, int orig_timeout);
|
void drop_bad_packets(UDPSocket &sock, int orig_timeout);
|
||||||
nsapi_version_t get_ip_version();
|
nsapi_version_t get_ip_version();
|
||||||
|
|
|
@ -29,14 +29,12 @@ namespace {
|
||||||
static const int SIGNAL_SIGIO_RX = 0x1;
|
static const int SIGNAL_SIGIO_RX = 0x1;
|
||||||
static const int SIGNAL_SIGIO_TX = 0x2;
|
static const int SIGNAL_SIGIO_TX = 0x2;
|
||||||
static const int SIGIO_TIMEOUT = 5000; //[ms]
|
static const int SIGIO_TIMEOUT = 5000; //[ms]
|
||||||
static const int WAIT2RECV_TIMEOUT = 5000; //[ms]
|
|
||||||
static const int RETRIES = 2;
|
static const int RETRIES = 2;
|
||||||
|
|
||||||
static const double EXPECTED_LOSS_RATIO = 0.0;
|
static const double EXPECTED_LOSS_RATIO = 0.0;
|
||||||
static const double TOLERATED_LOSS_RATIO = 0.3;
|
static const double TOLERATED_LOSS_RATIO = 0.3;
|
||||||
|
|
||||||
UDPSocket sock;
|
UDPSocket *sock;
|
||||||
Semaphore tx_sem(0, 1);
|
|
||||||
EventFlags signals;
|
EventFlags signals;
|
||||||
|
|
||||||
static const int BUFF_SIZE = 1200;
|
static const int BUFF_SIZE = 1200;
|
||||||
|
@ -60,8 +58,8 @@ static void _sigio_handler()
|
||||||
void UDPSOCKET_ECHOTEST()
|
void UDPSOCKET_ECHOTEST()
|
||||||
{
|
{
|
||||||
SocketAddress udp_addr;
|
SocketAddress udp_addr;
|
||||||
NetworkInterface::get_default_instance()->gethostbyname(MBED_CONF_APP_ECHO_SERVER_ADDR, &udp_addr);
|
NetworkInterface::get_default_instance()->gethostbyname(ECHO_SERVER_ADDR, &udp_addr);
|
||||||
udp_addr.set_port(MBED_CONF_APP_ECHO_SERVER_PORT);
|
udp_addr.set_port(ECHO_SERVER_PORT);
|
||||||
|
|
||||||
UDPSocket sock;
|
UDPSocket sock;
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.open(NetworkInterface::get_default_instance()));
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.open(NetworkInterface::get_default_instance()));
|
||||||
|
@ -110,72 +108,37 @@ void UDPSOCKET_ECHOTEST()
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close());
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close());
|
||||||
}
|
}
|
||||||
|
|
||||||
void udpsocket_echotest_nonblock_receiver(void *receive_bytes)
|
|
||||||
{
|
|
||||||
int expt2recv = *(int *)receive_bytes;
|
|
||||||
int recvd;
|
|
||||||
for (int retry_cnt = 0; retry_cnt <= RETRIES; retry_cnt++) {
|
|
||||||
recvd = sock.recvfrom(NULL, rx_buffer, expt2recv);
|
|
||||||
if (recvd == NSAPI_ERROR_WOULD_BLOCK) {
|
|
||||||
if (tc_exec_time.read() >= time_allotted) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
signals.wait_all(SIGNAL_SIGIO_RX, WAIT2RECV_TIMEOUT);
|
|
||||||
--retry_cnt;
|
|
||||||
continue;
|
|
||||||
} else if (recvd < 0) {
|
|
||||||
printf("sock.recvfrom returned %d\n", recvd);
|
|
||||||
TEST_FAIL();
|
|
||||||
break;
|
|
||||||
} else if (recvd == expt2recv) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
drop_bad_packets(sock, 0); // timeout equivalent to set_blocking(false)
|
|
||||||
|
|
||||||
tx_sem.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UDPSOCKET_ECHOTEST_NONBLOCK()
|
void UDPSOCKET_ECHOTEST_NONBLOCK()
|
||||||
{
|
{
|
||||||
tc_exec_time.start();
|
tc_exec_time.start();
|
||||||
time_allotted = split2half_rmng_udp_test_time(); // [s]
|
time_allotted = split2half_rmng_udp_test_time(); // [s]
|
||||||
|
|
||||||
SocketAddress udp_addr;
|
SocketAddress udp_addr;
|
||||||
NetworkInterface::get_default_instance()->gethostbyname(MBED_CONF_APP_ECHO_SERVER_ADDR, &udp_addr);
|
NetworkInterface::get_default_instance()->gethostbyname(ECHO_SERVER_ADDR, &udp_addr);
|
||||||
udp_addr.set_port(MBED_CONF_APP_ECHO_SERVER_PORT);
|
udp_addr.set_port(ECHO_SERVER_PORT);
|
||||||
|
sock = new UDPSocket();
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.open(NetworkInterface::get_default_instance()));
|
if (sock == NULL) {
|
||||||
sock.set_blocking(false);
|
TEST_FAIL_MESSAGE("UDPSocket not created");
|
||||||
sock.sigio(callback(_sigio_handler));
|
return;
|
||||||
|
}
|
||||||
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock->open(NetworkInterface::get_default_instance()));
|
||||||
|
sock->set_blocking(false);
|
||||||
|
sock->sigio(callback(_sigio_handler));
|
||||||
int sent;
|
int sent;
|
||||||
int packets_sent = 0;
|
int packets_sent = 0;
|
||||||
int packets_recv = 0;
|
int packets_recv = 0;
|
||||||
Thread *thread;
|
|
||||||
unsigned char *stack_mem = (unsigned char *)malloc(OS_STACK_SIZE);
|
|
||||||
TEST_ASSERT_NOT_NULL(stack_mem);
|
|
||||||
|
|
||||||
for (int s_idx = 0; s_idx < sizeof(pkt_sizes) / sizeof(*pkt_sizes); ++s_idx) {
|
for (int s_idx = 0; s_idx < sizeof(pkt_sizes) / sizeof(*pkt_sizes); ++s_idx) {
|
||||||
int pkt_s = pkt_sizes[s_idx];
|
int pkt_s = pkt_sizes[s_idx];
|
||||||
int packets_sent_prev = packets_sent;
|
int packets_sent_prev = packets_sent;
|
||||||
|
|
||||||
thread = new Thread(osPriorityNormal,
|
|
||||||
OS_STACK_SIZE,
|
|
||||||
stack_mem,
|
|
||||||
"receiver");
|
|
||||||
TEST_ASSERT_EQUAL(osOK, thread->start(callback(udpsocket_echotest_nonblock_receiver, &pkt_s)));
|
|
||||||
|
|
||||||
for (int retry_cnt = 0; retry_cnt <= RETRIES; retry_cnt++) {
|
for (int retry_cnt = 0; retry_cnt <= RETRIES; retry_cnt++) {
|
||||||
fill_tx_buffer_ascii(tx_buffer, pkt_s);
|
fill_tx_buffer_ascii(tx_buffer, pkt_s);
|
||||||
|
|
||||||
sent = sock.sendto(udp_addr, tx_buffer, pkt_s);
|
sent = sock->sendto(udp_addr, tx_buffer, pkt_s);
|
||||||
if (sent == pkt_s) {
|
if (sent == pkt_s) {
|
||||||
packets_sent++;
|
packets_sent++;
|
||||||
} else if (sent == NSAPI_ERROR_WOULD_BLOCK) {
|
} else if (sent == NSAPI_ERROR_WOULD_BLOCK) {
|
||||||
if (tc_exec_time.read() >= time_allotted ||
|
if (tc_exec_time.read() >= time_allotted ||
|
||||||
osSignalWait(SIGNAL_SIGIO_TX, SIGIO_TIMEOUT).status == osEventTimeout) {
|
signals.wait_all(SIGNAL_SIGIO_TX, SIGIO_TIMEOUT) == osFlagsErrorTimeout) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
--retry_cnt;
|
--retry_cnt;
|
||||||
|
@ -183,20 +146,36 @@ void UDPSOCKET_ECHOTEST_NONBLOCK()
|
||||||
printf("[Round#%02d - Sender] error, returned %d\n", s_idx, sent);
|
printf("[Round#%02d - Sender] error, returned %d\n", s_idx, sent);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!tx_sem.try_acquire_for(WAIT2RECV_TIMEOUT * 2)) { // RX might wait up to WAIT2RECV_TIMEOUT before recvfrom
|
|
||||||
continue;
|
int recvd;
|
||||||
|
for (int retry_recv = 0; retry_recv <= RETRIES; retry_recv++) {
|
||||||
|
recvd = sock->recvfrom(NULL, rx_buffer, pkt_s);
|
||||||
|
if (recvd == NSAPI_ERROR_WOULD_BLOCK) {
|
||||||
|
if (tc_exec_time.read() >= time_allotted) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
signals.wait_all(SIGNAL_SIGIO_RX, SIGIO_TIMEOUT);
|
||||||
|
--retry_recv;
|
||||||
|
continue;
|
||||||
|
} else if (recvd < 0) {
|
||||||
|
printf("sock.recvfrom returned %d\n", recvd);
|
||||||
|
TEST_FAIL();
|
||||||
|
break;
|
||||||
|
} else if (recvd == pkt_s) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recvd == pkt_s) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
// Make sure that at least one packet of every size was sent.
|
// Make sure that at least one packet of every size was sent.
|
||||||
TEST_ASSERT_TRUE(packets_sent > packets_sent_prev);
|
TEST_ASSERT_TRUE(packets_sent > packets_sent_prev);
|
||||||
thread->join();
|
|
||||||
delete thread;
|
|
||||||
if (memcmp(tx_buffer, rx_buffer, pkt_s) == 0) {
|
if (memcmp(tx_buffer, rx_buffer, pkt_s) == 0) {
|
||||||
packets_recv++;
|
packets_recv++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(stack_mem);
|
|
||||||
|
|
||||||
// Packet loss up to 30% tolerated
|
// Packet loss up to 30% tolerated
|
||||||
if (packets_sent > 0) {
|
if (packets_sent > 0) {
|
||||||
|
@ -220,6 +199,7 @@ void UDPSOCKET_ECHOTEST_NONBLOCK()
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close());
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock->close());
|
||||||
|
delete sock;
|
||||||
tc_exec_time.stop();
|
tc_exec_time.stop();
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,8 +71,8 @@ static void _sigio_handler(osThreadId id)
|
||||||
void UDPSOCKET_ECHOTEST_BURST()
|
void UDPSOCKET_ECHOTEST_BURST()
|
||||||
{
|
{
|
||||||
SocketAddress udp_addr;
|
SocketAddress udp_addr;
|
||||||
NetworkInterface::get_default_instance()->gethostbyname(MBED_CONF_APP_ECHO_SERVER_ADDR, &udp_addr);
|
NetworkInterface::get_default_instance()->gethostbyname(ECHO_SERVER_ADDR, &udp_addr);
|
||||||
udp_addr.set_port(MBED_CONF_APP_ECHO_SERVER_PORT);
|
udp_addr.set_port(ECHO_SERVER_PORT);
|
||||||
|
|
||||||
UDPSocket sock;
|
UDPSocket sock;
|
||||||
const int TIMEOUT = 5000; // [ms]
|
const int TIMEOUT = 5000; // [ms]
|
||||||
|
@ -154,8 +154,8 @@ void UDPSOCKET_ECHOTEST_BURST()
|
||||||
void UDPSOCKET_ECHOTEST_BURST_NONBLOCK()
|
void UDPSOCKET_ECHOTEST_BURST_NONBLOCK()
|
||||||
{
|
{
|
||||||
SocketAddress udp_addr;
|
SocketAddress udp_addr;
|
||||||
NetworkInterface::get_default_instance()->gethostbyname(MBED_CONF_APP_ECHO_SERVER_ADDR, &udp_addr);
|
NetworkInterface::get_default_instance()->gethostbyname(ECHO_SERVER_ADDR, &udp_addr);
|
||||||
udp_addr.set_port(MBED_CONF_APP_ECHO_SERVER_PORT);
|
udp_addr.set_port(ECHO_SERVER_PORT);
|
||||||
|
|
||||||
UDPSocket sock;
|
UDPSocket sock;
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.open(NetworkInterface::get_default_instance()));
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.open(NetworkInterface::get_default_instance()));
|
||||||
|
|
|
@ -38,8 +38,8 @@ static void _sigio_handler(osThreadId id)
|
||||||
void UDPSOCKET_RECV_TIMEOUT()
|
void UDPSOCKET_RECV_TIMEOUT()
|
||||||
{
|
{
|
||||||
SocketAddress udp_addr;
|
SocketAddress udp_addr;
|
||||||
NetworkInterface::get_default_instance()->gethostbyname(MBED_CONF_APP_ECHO_SERVER_ADDR, &udp_addr);
|
NetworkInterface::get_default_instance()->gethostbyname(ECHO_SERVER_ADDR, &udp_addr);
|
||||||
udp_addr.set_port(MBED_CONF_APP_ECHO_SERVER_PORT);
|
udp_addr.set_port(ECHO_SERVER_PORT);
|
||||||
|
|
||||||
static const int DATA_LEN = 100;
|
static const int DATA_LEN = 100;
|
||||||
char buff[DATA_LEN] = {0};
|
char buff[DATA_LEN] = {0};
|
||||||
|
|
|
@ -33,12 +33,12 @@ void UDPSOCKET_SENDTO_INVALID()
|
||||||
TEST_ASSERT(sock.sendto("", 9, NULL, 0) < 0);
|
TEST_ASSERT(sock.sendto("", 9, NULL, 0) < 0);
|
||||||
TEST_ASSERT(sock.sendto("", 0, NULL, 0) < 0);
|
TEST_ASSERT(sock.sendto("", 0, NULL, 0) < 0);
|
||||||
|
|
||||||
nsapi_error_t result = sock.sendto(MBED_CONF_APP_ECHO_SERVER_ADDR, 9, NULL, 0);
|
nsapi_error_t result = sock.sendto(ECHO_SERVER_ADDR, 9, NULL, 0);
|
||||||
if (result != NSAPI_ERROR_UNSUPPORTED) {
|
if (result != NSAPI_ERROR_UNSUPPORTED) {
|
||||||
TEST_ASSERT_EQUAL(0, result);
|
TEST_ASSERT_EQUAL(0, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_ASSERT_EQUAL(5, sock.sendto(MBED_CONF_APP_ECHO_SERVER_ADDR, 9, "hello", 5));
|
TEST_ASSERT_EQUAL(5, sock.sendto(ECHO_SERVER_ADDR, 9, "hello", 5));
|
||||||
|
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close());
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close());
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ void UDPSOCKET_SENDTO_REPEAT()
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.open(NetworkInterface::get_default_instance()));
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.open(NetworkInterface::get_default_instance()));
|
||||||
|
|
||||||
SocketAddress udp_addr;
|
SocketAddress udp_addr;
|
||||||
NetworkInterface::get_default_instance()->gethostbyname(MBED_CONF_APP_ECHO_SERVER_ADDR, &udp_addr);
|
NetworkInterface::get_default_instance()->gethostbyname(ECHO_SERVER_ADDR, &udp_addr);
|
||||||
udp_addr.set_port(9);
|
udp_addr.set_port(9);
|
||||||
|
|
||||||
int sent;
|
int sent;
|
||||||
|
|
|
@ -33,7 +33,7 @@ void UDPSOCKET_SENDTO_TIMEOUT()
|
||||||
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.open(NetworkInterface::get_default_instance()));
|
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.open(NetworkInterface::get_default_instance()));
|
||||||
|
|
||||||
SocketAddress udp_addr;
|
SocketAddress udp_addr;
|
||||||
NetworkInterface::get_default_instance()->gethostbyname(MBED_CONF_APP_ECHO_SERVER_ADDR, &udp_addr);
|
NetworkInterface::get_default_instance()->gethostbyname(ECHO_SERVER_ADDR, &udp_addr);
|
||||||
udp_addr.set_port(9);
|
udp_addr.set_port(9);
|
||||||
|
|
||||||
Timer timer;
|
Timer timer;
|
||||||
|
|
|
@ -131,6 +131,10 @@ void NETWORKINTERFACE_STATUS_NONBLOCK()
|
||||||
|
|
||||||
status = wait_status_callback();
|
status = wait_status_callback();
|
||||||
TEST_ASSERT_EQUAL(NSAPI_STATUS_DISCONNECTED, status);
|
TEST_ASSERT_EQUAL(NSAPI_STATUS_DISCONNECTED, status);
|
||||||
|
|
||||||
|
wait(1); // In cellular there might still come disconnected messages from the network which are sent to callback.
|
||||||
|
// This would cause this test to fail as next connect is already ongoing. So wait here a while until (hopefully)
|
||||||
|
// all messages also from the network have arrived.
|
||||||
}
|
}
|
||||||
|
|
||||||
net->attach(NULL);
|
net->attach(NULL);
|
||||||
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
# Testing the Mbed OS USB device
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
Before running tests, please make sure to use a
|
||||||
|
[top-level requirements.txt][LN-requirements] file to install all the
|
||||||
|
required Python modules.
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Additional, platform-specific setup is described below.
|
||||||
|
See also [Known issues](#known-issues).
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
1. Install a **generic USB driver** for two test devices.
|
||||||
|
1. Download `Zadig` application from [the Zadig website][LN-zadig].
|
||||||
|
1. Unplug the Mbed device.
|
||||||
|
1. Open `Zadig`.
|
||||||
|
1. Select *Device -> Load Preset Device*.
|
||||||
|
1. Open [TESTS/usb_device/basic/zadig_conf/mbed_os-usb_test_device1.cfg][LN-zadig_conf1]
|
||||||
|
1. Choose `libusb-win32 (v1.2.6.0)` driver.
|
||||||
|
1. Select `Install Driver` and click it.
|
||||||
|
1. Select *Device -> Load Preset Device*.
|
||||||
|
1. Open [TESTS/usb_device/basic/zadig_conf/mbed_os-usb_test_device2.cfg][LN-zadig_conf2]
|
||||||
|
1. Choose `libusb-win32 (v1.2.6.0)` driver.
|
||||||
|
1. Select `Install Driver` and click it.
|
||||||
|
1. Close `Zadig`.
|
||||||
|
1. Plug both USB interfaces (*DAPLink* and *USB device*).
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
1. Install the `hidapi` Python module, otherwise some USB HID test cases will
|
||||||
|
be skipped. This module is not installed during the initial setup due to
|
||||||
|
external dependencies for Linux.
|
||||||
|
|
||||||
|
For Debian-based Linux distros, the dependencies can be installed as follows
|
||||||
|
(based on module's [README][LN-hidapi_readme]):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
apt-get install python-dev libusb-1.0-0-dev libudev-dev
|
||||||
|
pip install --upgrade setuptools
|
||||||
|
```
|
||||||
|
|
||||||
|
To install the `hidapi` module itself, please use the attached
|
||||||
|
[requirements.txt][LN-hid_requirements] file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -r TESTS/usb_device/hid/requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Update the `udev` rules for Mbed USB CDC device as follows
|
||||||
|
([source][LN-udev_rules]):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo tee /etc/udev/rules.d/99-ttyacms.rules >/dev/null <<EOF
|
||||||
|
ATTRS{idVendor}=="1f00" ATTRS{idProduct}=="2013", ENV{ID_MM_DEVICE_IGNORE}="1"
|
||||||
|
ATTRS{idVendor}=="1f00" ATTRS{idProduct}=="2012", ENV{ID_MM_DEVICE_IGNORE}="1"
|
||||||
|
EOF
|
||||||
|
sudo udevadm control --reload-rules
|
||||||
|
```
|
||||||
|
|
||||||
|
This will prevent the `ModemManager` daemon from automatically opening the
|
||||||
|
port and sending the `AT commands`, which it does for every new
|
||||||
|
`/dev/ttyACM` device registered in system.
|
||||||
|
|
||||||
|
### Mac
|
||||||
|
No setup method has been verified for this platform.
|
||||||
|
|
||||||
|
## Running tests
|
||||||
|
1. Plug both USB interfaces (*DAPLink* and *USB device*) to your host machine.
|
||||||
|
1. Run tests:
|
||||||
|
```
|
||||||
|
mbed test -t <toolchain> -m <target> -n tests-usb_device-*
|
||||||
|
```
|
||||||
|
|
||||||
|
## Known issues
|
||||||
|
|
||||||
|
### Insufficient user permissions on a Linux machine
|
||||||
|
Some of the tests require privileged access to the USB device. Running these
|
||||||
|
as an unprivileged user will manifest with either of the following errors:
|
||||||
|
* ```
|
||||||
|
[HTST][INF] Device not found
|
||||||
|
```
|
||||||
|
* ```
|
||||||
|
[HTST][INF] TEST ERROR: Failed with "The device has no langid". Tried 20 times.
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Solution
|
||||||
|
Execute tests with elevated permissions using `sudo`:
|
||||||
|
```bash
|
||||||
|
mbed test -t <toolchain> -m <target> -n tests-usb_device-* --compile
|
||||||
|
sudo mbed test -t <toolchain> -m <target> -n tests-usb_device-* --run -v
|
||||||
|
```
|
||||||
|
Note only the `mbed test --run` command requires `sudo`. You can still
|
||||||
|
`mbed test --compile` as a normal user.
|
||||||
|
|
||||||
|
#### Alternative solution
|
||||||
|
Add an `udev` rule to set the ownership of the device node
|
||||||
|
[as shown here][LN-libusb_permissions].
|
||||||
|
|
||||||
|
### Data toggle reset test fails on a Linux machine
|
||||||
|
The `tests-usb_device-basic` / `"endpoint test data toggle reset"` test fails
|
||||||
|
with the following error:
|
||||||
|
```
|
||||||
|
[HTST][INF] TEST FAILED: Data toggle not reset when calling
|
||||||
|
ClearFeature(ENDPOINT_HALT) on an endpoint that has not been halted.
|
||||||
|
```
|
||||||
|
|
||||||
|
Implementations of the *xHCI* driver prior to version 4.17 of the Linux kernel did
|
||||||
|
not have the functionality necessary to test `"endpoint test data toggle reset"`.
|
||||||
|
Even though the data toggle is correctly reset on the device side, the host
|
||||||
|
side will not be synchronized and the test will falsely fail.
|
||||||
|
|
||||||
|
#### Solution
|
||||||
|
Make sure that **at least one** of the following prerequisites is met:
|
||||||
|
* using the Linux kernel ***4.17* or newer**,
|
||||||
|
* using the ***eHCI*** USB driver instead of *xHCI*.
|
||||||
|
|
||||||
|
Changing the USB driver may be as simple as updating one of the BIOS settings
|
||||||
|
on your machine. If you'd rather avoid rebooting, you can try
|
||||||
|
[using setpci command][LN-xhci_setpci].
|
||||||
|
|
||||||
|
#### Further reading
|
||||||
|
1. [the Linux kernel patch adding missing xHCI behavior][LN-linux_xhci_patch],
|
||||||
|
1. [LKML discussion explaining the details of this issue][LN-xhci_lkml_discussion].
|
||||||
|
|
||||||
|
### Mass storage tests are skipped on some targets
|
||||||
|
The `tests-usb_device-msd` test outputs the following message:
|
||||||
|
```
|
||||||
|
[CONN][RXD] SKIP: Not enough heap memory for HeapBlockDevice creation
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Solution
|
||||||
|
A device with at least *70 kB* of RAM is required to run this test.
|
||||||
|
The FAT32 filesystem cannot be mounted on a device smaller than 64 kB.
|
||||||
|
|
||||||
|
The test can be easily extended to use any block device available in Mbed.
|
||||||
|
|
||||||
|
### Windows 8/10: Mass storage tests are failing on test file read
|
||||||
|
By default Windows 8 and 10 access and write to removable drives shortly after they are connected. It's caused by drive indexing mechanisms. Because disk used in test has only 64kB its content can be easily corrupted by writing large files, what actually was encountered during test runs.
|
||||||
|
|
||||||
|
To prevent Windows from writing to removable drives on connect drive indexing can be turned off with the following procedure:
|
||||||
|
- Start the program "gpedit.msc"
|
||||||
|
- Navigate to "Computer Configuration \ Administrative Templates \ Windows Components \ Search"
|
||||||
|
- Enable the policy "Do not allow locations on removable drives to be added to libraries."
|
||||||
|
|
||||||
|
### Isochronous endpoints are skipped in loopback tests
|
||||||
|
#### Unresolved
|
||||||
|
|
||||||
|
### Serial tests fail intermittently on a Linux machine
|
||||||
|
#### Unresolved
|
||||||
|
You may want to connect the device directly to the host machine with no hubs on the way.
|
||||||
|
|
||||||
|
<!-- LINKS -->
|
||||||
|
[LN-requirements]: ../../requirements.txt
|
||||||
|
[LN-zadig]: https://zadig.akeo.ie/
|
||||||
|
[LN-zadig_conf1]: basic/zadig_conf/mbed_os-usb_test_device1.cfg
|
||||||
|
[LN-zadig_conf2]: basic/zadig_conf/mbed_os-usb_test_device2.cfg
|
||||||
|
[LN-hidapi_readme]: https://github.com/trezor/cython-hidapi/blob/master/README.rst#install
|
||||||
|
[LN-hid_requirements]: hid/requirements.txt
|
||||||
|
[LN-udev_rules]: https://linux-tips.com/t/prevent-modem-manager-to-capture-usb-serial-devices/284
|
||||||
|
[LN-libusb_permissions]: https://stackoverflow.com/questions/3738173/why-does-pyusb-libusb-require-root-sudo-permissions-on-linux/8582398#8582398
|
||||||
|
[LN-linux_xhci_patch]: https://github.com/torvalds/linux/commit/f5249461b504d35aa1a40140983b7ec415807d9e
|
||||||
|
[LN-xhci_lkml_discussion]: https://lkml.org/lkml/2016/12/15/388
|
||||||
|
[LN-xhci_setpci]: https://linuxmusicians.com/viewtopic.php?t=16901
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
# Testing the USB device data toggle reset with a Linux host
|
|
||||||
|
|
||||||
When you run the `tests-usb_device-basic` test suite on a Linux machine, please make
|
|
||||||
sure that at least one of the following prerequisites are met:
|
|
||||||
* using the Linux kernel ***4.17* or newer**,
|
|
||||||
* using the ***eHCI*** USB driver instead of *xHCI*.
|
|
||||||
|
|
||||||
Implementations of the *xHCI* driver prior to version 4.17 of the Linux kernel did
|
|
||||||
not have the functionality necessary to test `"endpoint test data toggle reset"`.
|
|
||||||
Even though the data toggle is correctly reset on the device side, the host side will
|
|
||||||
not be synchronized and the test will falsely fail.
|
|
||||||
|
|
||||||
Further reading:
|
|
||||||
1. [the Linux kernel patch adding missing xHCI behavior](https://github.com/torvalds/linux/commit/f5249461b504d35aa1a40140983b7ec415807d9e),
|
|
||||||
1. [LKML discussion explaining the details of this issue](https://lkml.org/lkml/2016/12/15/388).
|
|
|
@ -1,18 +0,0 @@
|
||||||
# Generic USB driver installation on Windows machines
|
|
||||||
|
|
||||||
In order to run the Mbed OS USB device test suite (`tests-usb_device-*`)
|
|
||||||
on Windows hosts you need to install generic USB drivers for two test devices.
|
|
||||||
|
|
||||||
1. Download *Zadig* application from https://zadig.akeo.ie/.
|
|
||||||
1. Unplug the Mbed device.
|
|
||||||
1. Open *Zadig*.
|
|
||||||
1. Select *Device -> Load Preset Device*.
|
|
||||||
1. Open `mbed_os-usb_test_device1.cfg`.
|
|
||||||
1. Choose `libusb-win32 (v1.2.6.0)` driver.
|
|
||||||
1. Select `Install Driver` and click it.
|
|
||||||
1. Select *Device -> Load Preset Device*.
|
|
||||||
1. Open `mbed_os-usb_test_device2.cfg`.
|
|
||||||
1. Choose `libusb-win32 (v1.2.6.0)` driver.
|
|
||||||
1. Select `Install Driver` and click it.
|
|
||||||
1. Close *Zadig*.
|
|
||||||
1. Plug both device USB interfaces (*DAPLink* and *USB device*).
|
|
|
@ -1,23 +0,0 @@
|
||||||
# Testing the USB HID device with a Linux host
|
|
||||||
|
|
||||||
Before running `tests-usb_device-hid` test suite on a Linux machine, please
|
|
||||||
make sure to install the `hidapi` Python module first, otherwise some test
|
|
||||||
cases will be skipped. Due to external dependencies for Linux, this module
|
|
||||||
is not installed during the initial setup, to keep the process as simple
|
|
||||||
as possible.
|
|
||||||
|
|
||||||
For Debian-based Linux distros, the dependencies can be installed as follows
|
|
||||||
(based on module's [README][1]):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
apt-get install python-dev libusb-1.0-0-dev libudev-dev
|
|
||||||
pip install --upgrade setuptools
|
|
||||||
```
|
|
||||||
To install the `hidapi` module itself, please use the attached
|
|
||||||
`TESTS/usb_device/hid/requirements.txt` file:
|
|
||||||
```bash
|
|
||||||
pip install -r requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
[1]: https://github.com/trezor/cython-hidapi/blob/master/README.rst#install
|
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
# USB mass storage test user guide
|
|
||||||
|
|
||||||
To run the tests-usb_device-msd test device with at least *70kB* of RAM is required.
|
|
||||||
Test creates 64kB `HeapBlockDevice` as block device and mounts FAT32 filesystem on it.
|
|
||||||
64kB block device is the smallest one that can mount FAT32 filesystem.
|
|
||||||
|
|
||||||
Test can be easily extended to use any block device available in Mbed
|
|
||||||
|
|
||||||
Test run command:
|
|
||||||
```bash
|
|
||||||
mbed test -t COMPILER -m TARGET -n tests-usb_device-msd
|
|
||||||
```
|
|
|
@ -33,6 +33,17 @@
|
||||||
#error [NOT_SUPPORTED] USB Device not supported for this target
|
#error [NOT_SUPPORTED] USB Device not supported for this target
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define OS_WINDOWS 1
|
||||||
|
#define OS_LINUX 2
|
||||||
|
#define OS_MAC 3
|
||||||
|
|
||||||
|
// Host side unmount was disabled for windows machines.
|
||||||
|
// PowerShell execution policies/restrictions cause that
|
||||||
|
// on some windows machines unmount is failing
|
||||||
|
// To re-enable it comment out below line.
|
||||||
|
#define DISABLE_HOST_SIDE_UMOUNT
|
||||||
|
|
||||||
#ifdef MIN
|
#ifdef MIN
|
||||||
#undef MIN
|
#undef MIN
|
||||||
#endif
|
#endif
|
||||||
|
@ -250,13 +261,10 @@ void msd_process(USBMSD *msd)
|
||||||
*/
|
*/
|
||||||
void storage_init()
|
void storage_init()
|
||||||
{
|
{
|
||||||
if (mbed_heap_size >= MIN_HEAP_SIZE) {
|
TEST_ASSERT_MESSAGE(mbed_heap_size >= MIN_HEAP_SIZE, "Not enough heap memory for HeapBlockDevice creation");
|
||||||
FATFileSystem::format(get_heap_block_device());
|
FATFileSystem::format(get_heap_block_device());
|
||||||
bool result = prepare_storage(get_heap_block_device(), &heap_fs);
|
bool result = prepare_storage(get_heap_block_device(), &heap_fs);
|
||||||
TEST_ASSERT_MESSAGE(result, "heap storage initialisation failed");
|
TEST_ASSERT_MESSAGE(result, "heap storage initialisation failed");
|
||||||
} else {
|
|
||||||
utest_printf("Not enough heap memory for HeapBlockDevice creation. Heap block device init skipped!!!\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Test mass storage device mount and unmount
|
/** Test mass storage device mount and unmount
|
||||||
|
@ -316,19 +324,31 @@ void mount_unmount_test(BlockDevice *bd, FileSystem *fs)
|
||||||
uint64_t ret_size = atoll(_key);
|
uint64_t ret_size = atoll(_key);
|
||||||
TEST_ASSERT_EQUAL_UINT64(get_fs_mount_size(fs), ret_size);
|
TEST_ASSERT_EQUAL_UINT64(get_fs_mount_size(fs), ret_size);
|
||||||
|
|
||||||
// unmount msd device on host side
|
#ifdef DISABLE_HOST_SIDE_UMOUNT
|
||||||
greentea_send_kv("unmount", 0);
|
greentea_send_kv("get_os_type", 0);
|
||||||
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
|
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
|
||||||
TEST_ASSERT_EQUAL_STRING_LOOP("passed", _key, i);
|
int32_t os_type = atoi(_value);
|
||||||
|
if (os_type != OS_WINDOWS) {
|
||||||
|
#endif
|
||||||
|
// unmount msd device on host side
|
||||||
|
greentea_send_kv("unmount", 0);
|
||||||
|
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
|
||||||
|
TEST_ASSERT_EQUAL_STRING_LOOP("passed", _key, i);
|
||||||
|
|
||||||
// wait for unmount event (set 10s timeout)
|
// wait for unmount event (set 10s timeout)
|
||||||
media_remove_event.wait(10000);
|
media_remove_event.wait(10000);
|
||||||
if (!usb.media_removed()) {
|
if (!usb.media_removed()) {
|
||||||
TEST_ASSERT_EQUAL_LOOP(true, usb.media_removed(), i);
|
TEST_ASSERT_EQUAL_LOOP(true, usb.media_removed(), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// unmount since media_removed doesn't disconnects device side
|
||||||
|
usb.disconnect();
|
||||||
|
#ifdef DISABLE_HOST_SIDE_UMOUNT
|
||||||
|
} else {
|
||||||
|
// unmount
|
||||||
|
usb.disconnect();
|
||||||
}
|
}
|
||||||
// unmount since media_removed doesn't disconnects device side
|
#endif
|
||||||
usb.disconnect();
|
|
||||||
|
|
||||||
// check if device is detached on host side
|
// check if device is detached on host side
|
||||||
greentea_send_kv("check_if_not_mounted", 0);
|
greentea_send_kv("check_if_not_mounted", 0);
|
||||||
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
|
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
|
||||||
|
@ -428,19 +448,13 @@ void mount_unmount_and_data_test(BlockDevice *bd, FileSystem *fs)
|
||||||
|
|
||||||
void heap_block_device_mount_unmount_test()
|
void heap_block_device_mount_unmount_test()
|
||||||
{
|
{
|
||||||
if (mbed_heap_size < MIN_HEAP_SIZE) {
|
TEST_ASSERT_MESSAGE(mbed_heap_size >= MIN_HEAP_SIZE, "Not enough heap memory for HeapBlockDevice creation");
|
||||||
TEST_SKIP_MESSAGE("Not enough heap memory for HeapBlockDevice creation");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mount_unmount_test<3>(get_heap_block_device(), &heap_fs);
|
mount_unmount_test<3>(get_heap_block_device(), &heap_fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void heap_block_device_mount_unmount_and_data_test()
|
void heap_block_device_mount_unmount_and_data_test()
|
||||||
{
|
{
|
||||||
if (mbed_heap_size < MIN_HEAP_SIZE) {
|
TEST_ASSERT_MESSAGE(mbed_heap_size >= MIN_HEAP_SIZE, "Not enough heap memory for HeapBlockDevice creation");
|
||||||
TEST_SKIP_MESSAGE("Not enough heap memory for HeapBlockDevice creation");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mount_unmount_and_data_test(get_heap_block_device(), &heap_fs);
|
mount_unmount_and_data_test(get_heap_block_device(), &heap_fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
# `udev` rules for Mbed USB CDC device
|
|
||||||
|
|
||||||
Before running `tests-usb_device-serial` test suite on Debian-based Linux
|
|
||||||
distros, make sure to [update the `udev` rules][1] as follows:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo tee /etc/udev/rules.d/99-ttyacms.rules >/dev/null <<EOF
|
|
||||||
ATTRS{idVendor}=="1f00" ATTRS{idProduct}=="2013", ENV{ID_MM_DEVICE_IGNORE}="1"
|
|
||||||
ATTRS{idVendor}=="1f00" ATTRS{idProduct}=="2012", ENV{ID_MM_DEVICE_IGNORE}="1"
|
|
||||||
EOF
|
|
||||||
sudo udevadm control --reload-rules
|
|
||||||
```
|
|
||||||
|
|
||||||
This will prevent the `ModemManager` daemon from automatically opening the port
|
|
||||||
and sending the `AT commands`, which it does for every new `/dev/ttyACM` device
|
|
||||||
registered in system.
|
|
||||||
|
|
||||||
[1]: https://linux-tips.com/t/prevent-modem-manager-to-capture-usb-serial-devices/284
|
|
|
@ -92,6 +92,7 @@ set(unittest-includes-base
|
||||||
"${PROJECT_SOURCE_DIR}/target_h/events"
|
"${PROJECT_SOURCE_DIR}/target_h/events"
|
||||||
"${PROJECT_SOURCE_DIR}/target_h/events/equeue"
|
"${PROJECT_SOURCE_DIR}/target_h/events/equeue"
|
||||||
"${PROJECT_SOURCE_DIR}/target_h/platform"
|
"${PROJECT_SOURCE_DIR}/target_h/platform"
|
||||||
|
"${PROJECT_SOURCE_DIR}/target_h/drivers"
|
||||||
"${PROJECT_SOURCE_DIR}/stubs"
|
"${PROJECT_SOURCE_DIR}/stubs"
|
||||||
"${PROJECT_SOURCE_DIR}/.."
|
"${PROJECT_SOURCE_DIR}/.."
|
||||||
"${PROJECT_SOURCE_DIR}/../features"
|
"${PROJECT_SOURCE_DIR}/../features"
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "Watchdog.h"
|
||||||
|
|
||||||
|
using namespace mbed;
|
||||||
|
extern bool testcase;
|
||||||
|
// AStyle ignored as the definition is not clear due to preprocessor usage
|
||||||
|
// *INDENT-OFF*
|
||||||
|
class TestWatchdog : public testing::Test {
|
||||||
|
protected:
|
||||||
|
|
||||||
|
void SetUp()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// *INDENT-ON*
|
||||||
|
|
||||||
|
TEST_F(TestWatchdog, test_watchdog_start_stop_get_timeout)
|
||||||
|
{
|
||||||
|
EXPECT_TRUE(Watchdog::get_instance().start(500));
|
||||||
|
EXPECT_FALSE(Watchdog::get_instance().start(2000));
|
||||||
|
EXPECT_TRUE(Watchdog::get_instance().stop());
|
||||||
|
EXPECT_FALSE(Watchdog::get_instance().stop());
|
||||||
|
EXPECT_EQ(500, Watchdog::get_instance().get_timeout());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TestWatchdog, test_watchdog_get_max_timeout)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(0xFFFFFFFF, Watchdog::get_instance().get_max_timeout());
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
####################
|
||||||
|
# UNIT TESTS
|
||||||
|
####################
|
||||||
|
set(TEST_SUITE_NAME "Watchdog")
|
||||||
|
|
||||||
|
# Add test specific include paths
|
||||||
|
set(unittest-includes ${unittest-includes}
|
||||||
|
.
|
||||||
|
../hal
|
||||||
|
)
|
||||||
|
|
||||||
|
# Source files
|
||||||
|
set(unittest-sources
|
||||||
|
../drivers/Watchdog.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test files
|
||||||
|
set(unittest-test-sources
|
||||||
|
drivers/Watchdog/test_watchdog.cpp
|
||||||
|
stubs/mbed_critical_stub.c
|
||||||
|
stubs/mbed_assert_stub.c
|
||||||
|
stubs/watchdog_api_stub.c
|
||||||
|
)
|
||||||
|
|
||||||
|
# defines
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDEVICE_WATCHDOG -DMBED_WDOG_ASSERT=1")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEVICE_WATCHDOG -DMBED_WDOG_ASSERT=1")
|
|
@ -603,15 +603,22 @@ TEST_F(TestAT_CellularContext, connect_disconnect_async)
|
||||||
ASSERT_EQ(network_cb_count, 5);
|
ASSERT_EQ(network_cb_count, 5);
|
||||||
ASSERT_EQ(ctx1.connect(), NSAPI_ERROR_IS_CONNECTED);
|
ASSERT_EQ(ctx1.connect(), NSAPI_ERROR_IS_CONNECTED);
|
||||||
EXPECT_TRUE(ctx1.is_connected() == true);
|
EXPECT_TRUE(ctx1.is_connected() == true);
|
||||||
|
ASSERT_EQ(ctx1.disconnect(), NSAPI_ERROR_NO_MEMORY);
|
||||||
|
EXPECT_TRUE(ctx1.is_connected() == true);
|
||||||
|
|
||||||
|
struct equeue_event ptr;
|
||||||
|
equeue_stub.void_ptr = &ptr;
|
||||||
|
equeue_stub.call_cb_immediately = true;
|
||||||
ASSERT_EQ(ctx1.disconnect(), NSAPI_ERROR_OK);
|
ASSERT_EQ(ctx1.disconnect(), NSAPI_ERROR_OK);
|
||||||
EXPECT_TRUE(ctx1.is_connected() == false);
|
EXPECT_TRUE(ctx1.is_connected() == false);
|
||||||
|
|
||||||
// sdet CellularDevice_stub::connect_counter = 0 so device is already attached and will return NSAPI_ERROR_ALREADY to context when calling connect
|
// sdet CellularDevice_stub::connect_counter = 0 so device is already attached and will return NSAPI_ERROR_ALREADY to context when calling connect
|
||||||
|
equeue_stub.void_ptr = &ptr;
|
||||||
|
equeue_stub.call_cb_immediately = false;
|
||||||
CellularDevice_stub::connect_counter = 0;
|
CellularDevice_stub::connect_counter = 0;
|
||||||
// queue can't allocate so return NSAPI_ERROR_NO_MEMORY
|
// queue can't allocate so return NSAPI_ERROR_NO_MEMORY
|
||||||
ASSERT_EQ(ctx1.connect(), NSAPI_ERROR_NO_MEMORY);
|
ASSERT_EQ(ctx1.connect(), NSAPI_ERROR_NO_MEMORY);
|
||||||
|
|
||||||
struct equeue_event ptr;
|
|
||||||
equeue_stub.void_ptr = &ptr;
|
equeue_stub.void_ptr = &ptr;
|
||||||
equeue_stub.call_cb_immediately = true;
|
equeue_stub.call_cb_immediately = true;
|
||||||
ASSERT_EQ(ctx1.connect(), NSAPI_ERROR_OK);
|
ASSERT_EQ(ctx1.connect(), NSAPI_ERROR_OK);
|
||||||
|
|
|
@ -565,15 +565,15 @@ TEST_F(TestATHandler, test_ATHandler_read_bytes)
|
||||||
mbed_poll_stub::int_value = 1;;
|
mbed_poll_stub::int_value = 1;;
|
||||||
|
|
||||||
// Read 5 bytes
|
// Read 5 bytes
|
||||||
EXPECT_TRUE(5 == at.read_bytes(buf, 5));
|
EXPECT_EQ(5, at.read_bytes(buf, 5));
|
||||||
EXPECT_TRUE(!memcmp(buf, table1, 5));
|
EXPECT_TRUE(!memcmp(buf, table1, 5));
|
||||||
// get_char triggered above should have filled in the whole reading buffer(fill_buffer())
|
// get_char triggered above should have filled in the whole reading buffer(fill_buffer())
|
||||||
EXPECT_TRUE(filehandle_stub_table_pos == (strlen(table1) - 1));
|
EXPECT_EQ(filehandle_stub_table_pos, (strlen(table1)));
|
||||||
// Read another 8 bytes
|
// Read another 8 bytes
|
||||||
EXPECT_TRUE(8 == at.read_bytes(buf, 8) && !memcmp(buf, table1 + 5, 8));
|
EXPECT_TRUE(8 == at.read_bytes(buf, 8) && !memcmp(buf, table1 + 5, 8));
|
||||||
// Reading more than the 4 bytes left -> ERROR
|
// Reading more than the 4 bytes left -> ERROR
|
||||||
EXPECT_TRUE(-1 == at.read_bytes(buf, 5));
|
EXPECT_EQ(-1, at.read_bytes(buf, 5));
|
||||||
EXPECT_TRUE(NSAPI_ERROR_DEVICE_ERROR == at.get_last_error());
|
EXPECT_EQ(NSAPI_ERROR_DEVICE_ERROR, at.get_last_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TestATHandler, test_ATHandler_read_string)
|
TEST_F(TestATHandler, test_ATHandler_read_string)
|
||||||
|
|
|
@ -35,6 +35,7 @@ set(unittest-test-sources
|
||||||
stubs/cipher_stub.c
|
stubs/cipher_stub.c
|
||||||
stubs/aes_stub.c
|
stubs/aes_stub.c
|
||||||
stubs/cmac_stub.c
|
stubs/cmac_stub.c
|
||||||
|
stubs/mbed_assert_stub.c
|
||||||
../features/nanostack/coap-service/test/coap-service/unittest/stub/mbedtls_stub.c
|
../features/nanostack/coap-service/test/coap-service/unittest/stub/mbedtls_stub.c
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -20,4 +20,5 @@ set(unittest-test-sources
|
||||||
stubs/EventFlags_stub.cpp
|
stubs/EventFlags_stub.cpp
|
||||||
stubs/Mutex_stub.cpp
|
stubs/Mutex_stub.cpp
|
||||||
stubs/CellularContext_stub.cpp
|
stubs/CellularContext_stub.cpp
|
||||||
|
stubs/mbed_assert_stub.c
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#include "watchdog_api.h"
|
||||||
|
|
||||||
|
#if DEVICE_WATCHDOG
|
||||||
|
|
||||||
|
static uint32_t _timeout = 0;
|
||||||
|
|
||||||
|
watchdog_status_t hal_watchdog_init(const watchdog_config_t *config)
|
||||||
|
{
|
||||||
|
_timeout = config->timeout_ms;
|
||||||
|
return WATCHDOG_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hal_watchdog_kick(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
watchdog_status_t hal_watchdog_stop(void)
|
||||||
|
{
|
||||||
|
return WATCHDOG_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t hal_watchdog_get_reload_value(void)
|
||||||
|
{
|
||||||
|
return _timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
watchdog_features_t hal_watchdog_get_platform_features(void)
|
||||||
|
{
|
||||||
|
watchdog_features_t features;
|
||||||
|
features.max_timeout = 0xFFFFFFFF;
|
||||||
|
return features;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DEVICE_WATCHDOG
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#ifndef MBED_LOWPOWERTICKER_H
|
||||||
|
#define MBED_LOWPOWERTICKER_H
|
||||||
|
|
||||||
|
#include "hal/ticker_api.h"
|
||||||
|
#include "Callback.h"
|
||||||
|
|
||||||
|
namespace mbed {
|
||||||
|
/** \addtogroup drivers */
|
||||||
|
|
||||||
|
/** mock Low Power Ticker
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class LowPowerTicker {
|
||||||
|
|
||||||
|
public:
|
||||||
|
LowPowerTicker()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~LowPowerTicker()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void attach_us(Callback<void()> func, us_timestamp_t t)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
void detach()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mbed
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2019 Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#ifndef MBED_TICKER_H
|
||||||
|
#define MBED_TICKER_H
|
||||||
|
#include "drivers/TimerEvent.h"
|
||||||
|
#include "platform/Callback.h"
|
||||||
|
|
||||||
|
namespace mbed {
|
||||||
|
/** \addtogroup drivers */
|
||||||
|
|
||||||
|
/** mock Ticker
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class Ticker {
|
||||||
|
|
||||||
|
public:
|
||||||
|
Ticker()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void attach_us(Callback<void()> func, us_timestamp_t t)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void detach()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
~Ticker()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mbed
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,135 @@
|
||||||
|
|
||||||
|
/** \addtogroup platform */
|
||||||
|
/** @{*/
|
||||||
|
/**
|
||||||
|
* \defgroup platform_Assert Assert macros
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018--2019 Arm Limited and affiliates.
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#ifndef MBED_ASSERT_H
|
||||||
|
#define MBED_ASSERT_H
|
||||||
|
|
||||||
|
#include "mbed_preprocessor.h"
|
||||||
|
#include "mbed_toolchain.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Internal mbed assert function which is invoked when MBED_ASSERT macro fails.
|
||||||
|
* This function is active only if NDEBUG is not defined prior to including this
|
||||||
|
* assert header file.
|
||||||
|
* In case of MBED_ASSERT failing condition, error() is called with the assertation message.
|
||||||
|
* @param expr Expression to be checked.
|
||||||
|
* @param file File where assertation failed.
|
||||||
|
* @param line Failing assertation line number.
|
||||||
|
*/
|
||||||
|
MBED_NORETURN void mbed_assert_internal(const char *expr, const char *file, int line);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** MBED_ASSERT
|
||||||
|
* Declare runtime assertions: results in runtime error if condition is false
|
||||||
|
*
|
||||||
|
* @note
|
||||||
|
* Use of MBED_ASSERT is limited to Debug and Develop builds.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
*
|
||||||
|
* int Configure(serial_t *obj) {
|
||||||
|
* MBED_ASSERT(obj);
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
#if defined( NDEBUG ) && !defined (MBED_WDOG_ASSERT)
|
||||||
|
#define MBED_ASSERT(expr) ((void)0)
|
||||||
|
#else
|
||||||
|
#define MBED_ASSERT(expr) \
|
||||||
|
do { \
|
||||||
|
if (!(expr)) { \
|
||||||
|
mbed_assert_internal(#expr, __FILE__, __LINE__); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** MBED_STATIC_ASSERT
|
||||||
|
* Declare compile-time assertions, results in compile-time error if condition is false
|
||||||
|
*
|
||||||
|
* The assertion acts as a declaration that can be placed at file scope, in a
|
||||||
|
* code block (except after a label), or as a member of a C++ class/struct/union.
|
||||||
|
*
|
||||||
|
* @note
|
||||||
|
* Use of MBED_STATIC_ASSERT as a member of a struct/union is limited:
|
||||||
|
* - In C++, MBED_STATIC_ASSERT is valid in class/struct/union scope.
|
||||||
|
* - In C, MBED_STATIC_ASSERT is not valid in struct/union scope, and
|
||||||
|
* MBED_STRUCT_STATIC_ASSERT is provided as an alternative that is valid
|
||||||
|
* in C and C++ class/struct/union scope.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* MBED_STATIC_ASSERT(MBED_LIBRARY_VERSION >= 120,
|
||||||
|
* "The mbed library must be at least version 120");
|
||||||
|
*
|
||||||
|
* int main() {
|
||||||
|
* MBED_STATIC_ASSERT(sizeof(int) >= sizeof(char),
|
||||||
|
* "An int must be larger than a char");
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
#if defined(__cplusplus) && (__cplusplus >= 201103L || __cpp_static_assert >= 200410L)
|
||||||
|
#define MBED_STATIC_ASSERT(expr, msg) static_assert(expr, msg)
|
||||||
|
#elif !defined(__cplusplus) && __STDC_VERSION__ >= 201112L
|
||||||
|
#define MBED_STATIC_ASSERT(expr, msg) _Static_assert(expr, msg)
|
||||||
|
#elif defined(__cplusplus) && defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__) \
|
||||||
|
&& (__GNUC__*100 + __GNUC_MINOR__) > 403L
|
||||||
|
#define MBED_STATIC_ASSERT(expr, msg) __extension__ static_assert(expr, msg)
|
||||||
|
#elif !defined(__cplusplus) && defined(__GNUC__) && !defined(__CC_ARM) \
|
||||||
|
&& (__GNUC__*100 + __GNUC_MINOR__) > 406L
|
||||||
|
#define MBED_STATIC_ASSERT(expr, msg) __extension__ _Static_assert(expr, msg)
|
||||||
|
#elif defined(__ICCARM__)
|
||||||
|
#define MBED_STATIC_ASSERT(expr, msg) static_assert(expr, msg)
|
||||||
|
#else
|
||||||
|
#define MBED_STATIC_ASSERT(expr, msg) \
|
||||||
|
enum {MBED_CONCAT(MBED_ASSERTION_AT_, __LINE__) = sizeof(char[(expr) ? 1 : -1])}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** MBED_STRUCT_STATIC_ASSERT
|
||||||
|
* Declare compile-time assertions, results in compile-time error if condition is false
|
||||||
|
*
|
||||||
|
* Unlike MBED_STATIC_ASSERT, MBED_STRUCT_STATIC_ASSERT can and must be used
|
||||||
|
* as a member of a C/C++ class/struct/union.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* struct thing {
|
||||||
|
* MBED_STATIC_ASSERT(2 + 2 == 4,
|
||||||
|
* "Hopefully the universe is mathematically consistent");
|
||||||
|
* };
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
#define MBED_STRUCT_STATIC_ASSERT(expr, msg) int : (expr) ? 0 : -1
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
|
@ -15,3 +15,18 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/** Resets the processor and most of the sub-system
|
||||||
|
*
|
||||||
|
* @note Does not affect the debug sub-system
|
||||||
|
*/
|
||||||
|
#ifndef MBED_POWER_MGMT_H
|
||||||
|
#define MBED_POWER_MGMT_H
|
||||||
|
extern void mock_system_reset();
|
||||||
|
|
||||||
|
MBED_NORETURN static inline void system_reset(void)
|
||||||
|
{
|
||||||
|
mock_system_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,9 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if defined(MBED_CONF_NANOSTACK_CONFIGURATION) && DEVICE_SPI && DEVICE_INTERRUPTIN && defined(MBED_CONF_RTOS_PRESENT)
|
||||||
|
|
||||||
#include "nanostack/platform/arm_hal_phy.h"
|
#include "nanostack/platform/arm_hal_phy.h"
|
||||||
#include "rf_configuration.h"
|
#include "rf_configuration.h"
|
||||||
|
|
||||||
|
@ -167,3 +170,6 @@ uint32_t rf_conf_calculate_deviation(phy_modulation_index_e modulation_index, ui
|
||||||
}
|
}
|
||||||
return deviation;
|
return deviation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // MBED_CONF_NANOSTACK_CONFIGURATION && DEVICE_SPI && DEVICE_INTERRUPTIN && defined(MBED_CONF_RTOS_PRESENT)
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,12 @@
|
||||||
* See BSD-3-Clause license in README.md
|
* See BSD-3-Clause license in README.md
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if !defined(MBEDTLS_CONFIG_FILE)
|
||||||
|
#include "mbedtls/config.h"
|
||||||
|
#else
|
||||||
|
#include MBEDTLS_CONFIG_FILE
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "t_cose_crypto.h"
|
#include "t_cose_crypto.h"
|
||||||
#include "tfm_plat_defs.h"
|
#include "tfm_plat_defs.h"
|
||||||
#include "psa/crypto.h"
|
#include "psa/crypto.h"
|
||||||
|
|
|
@ -19,6 +19,12 @@
|
||||||
* This file is part of mbed TLS (https://tls.mbed.org)
|
* This file is part of mbed TLS (https://tls.mbed.org)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if !defined(MBEDTLS_CONFIG_FILE)
|
||||||
|
#include "mbedtls/config.h"
|
||||||
|
#else
|
||||||
|
#include MBEDTLS_CONFIG_FILE
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(MBEDTLS_PSA_CRYPTO_C)
|
#if defined(MBEDTLS_PSA_CRYPTO_C)
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
|
@ -127,7 +127,7 @@ static psa_status_t convert_status(int status)
|
||||||
* \param n[in] number of bits to shift right
|
* \param n[in] number of bits to shift right
|
||||||
* \return the result
|
* \return the result
|
||||||
*/
|
*/
|
||||||
MBED_FORCEINLINE uint32_t lsr32(uint32_t x, uint32_t n)
|
static MBED_FORCEINLINE uint32_t lsr32(uint32_t x, uint32_t n)
|
||||||
{
|
{
|
||||||
return x >> n;
|
return x >> n;
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ MBED_FORCEINLINE uint32_t lsr32(uint32_t x, uint32_t n)
|
||||||
* \param n[in] number of bits to shift right
|
* \param n[in] number of bits to shift right
|
||||||
* \return the result
|
* \return the result
|
||||||
*/
|
*/
|
||||||
MBED_FORCEINLINE uint64_t lsr64(uint64_t x, uint32_t n)
|
static MBED_FORCEINLINE uint64_t lsr64(uint64_t x, uint32_t n)
|
||||||
{
|
{
|
||||||
return x >> n;
|
return x >> n;
|
||||||
}
|
}
|
||||||
|
@ -185,12 +185,8 @@ static void generate_fn(char *tdb_filename, uint32_t tdb_filename_size, psa_stor
|
||||||
|
|
||||||
psa_status_t psa_storage_set_impl(KVStore *kvstore, int32_t pid, psa_storage_uid_t uid,
|
psa_status_t psa_storage_set_impl(KVStore *kvstore, int32_t pid, psa_storage_uid_t uid,
|
||||||
uint32_t data_length, const void *p_data,
|
uint32_t data_length, const void *p_data,
|
||||||
psa_storage_create_flags_t create_flags)
|
uint32_t kv_create_flags)
|
||||||
{
|
{
|
||||||
if ((create_flags & (~FLAGS_MSK)) != 0) {
|
|
||||||
return PSA_ERROR_NOT_SUPPORTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uid == 0) {
|
if (uid == 0) {
|
||||||
return PSA_ERROR_INVALID_ARGUMENT;
|
return PSA_ERROR_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
@ -198,11 +194,6 @@ psa_status_t psa_storage_set_impl(KVStore *kvstore, int32_t pid, psa_storage_uid
|
||||||
char kv_key[PSA_STORAGE_FILE_NAME_MAX] = {'\0'};
|
char kv_key[PSA_STORAGE_FILE_NAME_MAX] = {'\0'};
|
||||||
generate_fn(kv_key, PSA_STORAGE_FILE_NAME_MAX, uid, pid);
|
generate_fn(kv_key, PSA_STORAGE_FILE_NAME_MAX, uid, pid);
|
||||||
|
|
||||||
uint32_t kv_create_flags = 0;
|
|
||||||
if (create_flags & PSA_STORAGE_FLAG_WRITE_ONCE) {
|
|
||||||
kv_create_flags = KVStore::WRITE_ONCE_FLAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
int status = kvstore->set(kv_key, p_data, data_length, kv_create_flags);
|
int status = kvstore->set(kv_key, p_data, data_length, kv_create_flags);
|
||||||
|
|
||||||
return convert_status(status);
|
return convert_status(status);
|
||||||
|
|
|
@ -36,7 +36,7 @@ typedef psa_status_t (*migrate_func_t)(mbed::KVStore *kvstore, const psa_storage
|
||||||
|
|
||||||
void psa_storage_handle_version(mbed::KVStore *kvstore, const char *version_key, const psa_storage_version_t *version,
|
void psa_storage_handle_version(mbed::KVStore *kvstore, const char *version_key, const psa_storage_version_t *version,
|
||||||
migrate_func_t migrate_func);
|
migrate_func_t migrate_func);
|
||||||
psa_status_t psa_storage_set_impl(mbed::KVStore *kvstore, int32_t pid, psa_storage_uid_t uid, uint32_t data_length, const void *p_data, psa_storage_create_flags_t create_flags);
|
psa_status_t psa_storage_set_impl(mbed::KVStore *kvstore, int32_t pid, psa_storage_uid_t uid, uint32_t data_length, const void *p_data, uint32_t kv_create_flags);
|
||||||
psa_status_t psa_storage_get_impl(mbed::KVStore *kvstore, int32_t pid, psa_storage_uid_t uid, uint32_t data_offset, uint32_t data_length, void *p_data);
|
psa_status_t psa_storage_get_impl(mbed::KVStore *kvstore, int32_t pid, psa_storage_uid_t uid, uint32_t data_offset, uint32_t data_length, void *p_data);
|
||||||
psa_status_t psa_storage_get_info_impl(mbed::KVStore *kvstore, int32_t pid, psa_storage_uid_t uid, struct psa_storage_info_t *p_info);
|
psa_status_t psa_storage_get_info_impl(mbed::KVStore *kvstore, int32_t pid, psa_storage_uid_t uid, struct psa_storage_info_t *p_info);
|
||||||
psa_status_t psa_storage_remove_impl(mbed::KVStore *kvstore, int32_t pid, psa_storage_uid_t uid);
|
psa_status_t psa_storage_remove_impl(mbed::KVStore *kvstore, int32_t pid, psa_storage_uid_t uid);
|
||||||
|
|
|
@ -87,6 +87,10 @@ psa_status_t psa_its_set_impl(int32_t pid, psa_storage_uid_t uid, uint32_t data_
|
||||||
its_init();
|
its_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (create_flags & ~PSA_STORAGE_FLAG_WRITE_ONCE) {
|
||||||
|
return PSA_ERROR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
return psa_storage_set_impl(kvstore, pid, uid, data_length, p_data, create_flags);
|
return psa_storage_set_impl(kvstore, pid, uid, data_length, p_data, create_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ extern "C"
|
||||||
#define PSA_PS_GLOBAL_PID 1
|
#define PSA_PS_GLOBAL_PID 1
|
||||||
|
|
||||||
static KVStore *kvstore = NULL;
|
static KVStore *kvstore = NULL;
|
||||||
|
static uint32_t def_kvstore_flags = 0;
|
||||||
|
|
||||||
MBED_WEAK psa_status_t ps_version_migrate(KVStore *kvstore,
|
MBED_WEAK psa_status_t ps_version_migrate(KVStore *kvstore,
|
||||||
const psa_storage_version_t *old_version, const psa_storage_version_t *new_version)
|
const psa_storage_version_t *old_version, const psa_storage_version_t *new_version)
|
||||||
|
@ -61,12 +62,18 @@ static void ps_init(void)
|
||||||
KVMap &kv_map = KVMap::get_instance();
|
KVMap &kv_map = KVMap::get_instance();
|
||||||
psa_storage_version_t version = {PSA_PS_API_VERSION_MAJOR, PSA_PS_API_VERSION_MINOR};
|
psa_storage_version_t version = {PSA_PS_API_VERSION_MAJOR, PSA_PS_API_VERSION_MINOR};
|
||||||
kvstore = kv_map.get_main_kv_instance(STR_EXPAND(MBED_CONF_STORAGE_DEFAULT_KV));
|
kvstore = kv_map.get_main_kv_instance(STR_EXPAND(MBED_CONF_STORAGE_DEFAULT_KV));
|
||||||
if (!kvstore) {
|
KVStore *int_kvstore = kv_map.get_internal_kv_instance(STR_EXPAND(MBED_CONF_STORAGE_DEFAULT_KV));;
|
||||||
|
if (!kvstore || !int_kvstore) {
|
||||||
// Can only happen due to system misconfiguration.
|
// Can only happen due to system misconfiguration.
|
||||||
// Thus considered as unrecoverable error for runtime.
|
// Thus considered as unrecoverable error for runtime.
|
||||||
error("Failed getting kvstore instance\n");
|
error("Failed getting kvstore instance\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def_kvstore_flags = 0;
|
||||||
|
if (kvstore != int_kvstore) {
|
||||||
|
def_kvstore_flags = KVStore::REQUIRE_CONFIDENTIALITY_FLAG | KVStore::REQUIRE_REPLAY_PROTECTION_FLAG;
|
||||||
|
}
|
||||||
|
|
||||||
psa_storage_handle_version(kvstore, PS_VERSION_KEY, &version, ps_version_migrate);
|
psa_storage_handle_version(kvstore, PS_VERSION_KEY, &version, ps_version_migrate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +90,16 @@ psa_status_t psa_ps_set(psa_storage_uid_t uid, uint32_t data_length, const void
|
||||||
ps_init();
|
ps_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
return psa_storage_set_impl(kvstore, PSA_PS_GLOBAL_PID, uid, data_length, p_data, create_flags);
|
if (create_flags & ~PSA_STORAGE_FLAG_WRITE_ONCE) {
|
||||||
|
return PSA_ERROR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t kv_create_flags = def_kvstore_flags;
|
||||||
|
if (create_flags & PSA_STORAGE_FLAG_WRITE_ONCE) {
|
||||||
|
kv_create_flags |= KVStore::WRITE_ONCE_FLAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
return psa_storage_set_impl(kvstore, PSA_PS_GLOBAL_PID, uid, data_length, p_data, kv_create_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
psa_status_t psa_ps_get(psa_storage_uid_t uid, uint32_t data_offset, uint32_t data_length, void *p_data)
|
psa_status_t psa_ps_get(psa_storage_uid_t uid, uint32_t data_offset, uint32_t data_length, void *p_data)
|
||||||
|
|
|
@ -14,6 +14,10 @@
|
||||||
"REALTEK_RTL8195AM": {
|
"REALTEK_RTL8195AM": {
|
||||||
"base-address": "0x1C0000",
|
"base-address": "0x1C0000",
|
||||||
"size": "0x40000"
|
"size": "0x40000"
|
||||||
|
},
|
||||||
|
"FVP_MPS2": {
|
||||||
|
"base-address": "0x00200000",
|
||||||
|
"size": "0x200000"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ using namespace mbed;
|
||||||
#define SPIF_BASIC_PARAM_TABLE_PAGE_SIZE_BYTE 40
|
#define SPIF_BASIC_PARAM_TABLE_PAGE_SIZE_BYTE 40
|
||||||
// Address Length
|
// Address Length
|
||||||
#define SPIF_ADDR_SIZE_3_BYTES 3
|
#define SPIF_ADDR_SIZE_3_BYTES 3
|
||||||
|
#define SPIF_ADDR_SIZE_4_BYTES 4
|
||||||
// Erase Types Params
|
// Erase Types Params
|
||||||
#define SPIF_BASIC_PARAM_ERASE_TYPE_1_BYTE 29
|
#define SPIF_BASIC_PARAM_ERASE_TYPE_1_BYTE 29
|
||||||
#define SPIF_BASIC_PARAM_ERASE_TYPE_2_BYTE 31
|
#define SPIF_BASIC_PARAM_ERASE_TYPE_2_BYTE 31
|
||||||
|
@ -88,7 +89,9 @@ enum spif_default_instructions {
|
||||||
SPIF_RSTEN = 0x66, // Reset Enable
|
SPIF_RSTEN = 0x66, // Reset Enable
|
||||||
SPIF_RST = 0x99, // Reset
|
SPIF_RST = 0x99, // Reset
|
||||||
SPIF_RDID = 0x9f, // Read Manufacturer and JDEC Device ID
|
SPIF_RDID = 0x9f, // Read Manufacturer and JDEC Device ID
|
||||||
SPIF_ULBPR = 0x98, // Clears all write-protection bits in the Block-Protection register
|
SPIF_ULBPR = 0x98, // Clears all write-protection bits in the Block-Protection register,
|
||||||
|
SPIF_4BEN = 0xB7, // Enable 4-byte address mode
|
||||||
|
SPIF_4BDIS = 0xE9, // Disable 4-byte address mode
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mutex is used for some SPI Driver commands that must be done sequentially with no other commands in between
|
// Mutex is used for some SPI Driver commands that must be done sequentially with no other commands in between
|
||||||
|
@ -117,7 +120,7 @@ SPIFBlockDevice::SPIFBlockDevice(
|
||||||
_region_erase_types_bitfield[0] = ERASE_BITMASK_NONE;
|
_region_erase_types_bitfield[0] = ERASE_BITMASK_NONE;
|
||||||
|
|
||||||
if (SPIF_BD_ERROR_OK != _spi_set_frequency(freq)) {
|
if (SPIF_BD_ERROR_OK != _spi_set_frequency(freq)) {
|
||||||
tr_error("ERROR: SPI Set Frequency Failed");
|
tr_error("SPI Set Frequency Failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
_cs = 1;
|
_cs = 1;
|
||||||
|
@ -148,19 +151,16 @@ int SPIFBlockDevice::init()
|
||||||
|
|
||||||
// Soft Reset
|
// Soft Reset
|
||||||
if (-1 == _reset_flash_mem()) {
|
if (-1 == _reset_flash_mem()) {
|
||||||
tr_error("ERROR: init - Unable to initialize flash memory, tests failed\n");
|
tr_error("init - Unable to initialize flash memory, tests failed");
|
||||||
status = SPIF_BD_ERROR_DEVICE_ERROR;
|
status = SPIF_BD_ERROR_DEVICE_ERROR;
|
||||||
goto exit_point;
|
goto exit_point;
|
||||||
} else {
|
|
||||||
tr_info("INFO: Initialize flash memory OK\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Read Manufacturer ID (1byte), and Device ID (2bytes)*/
|
/* Read Manufacturer ID (1byte), and Device ID (2bytes)*/
|
||||||
spi_status = _spi_send_general_command(SPIF_RDID, SPI_NO_ADDRESS_COMMAND, NULL, 0, (char *)vendor_device_ids,
|
spi_status = _spi_send_general_command(SPIF_RDID, SPI_NO_ADDRESS_COMMAND, NULL, 0, (char *)vendor_device_ids,
|
||||||
data_length);
|
data_length);
|
||||||
if (spi_status != SPIF_BD_ERROR_OK) {
|
if (spi_status != SPIF_BD_ERROR_OK) {
|
||||||
tr_error("ERROR: init - Read Vendor ID Failed");
|
tr_error("init - Read Vendor ID Failed");
|
||||||
status = SPIF_BD_ERROR_DEVICE_ERROR;
|
status = SPIF_BD_ERROR_DEVICE_ERROR;
|
||||||
goto exit_point;
|
goto exit_point;
|
||||||
}
|
}
|
||||||
|
@ -176,14 +176,14 @@ int SPIFBlockDevice::init()
|
||||||
|
|
||||||
//Synchronize Device
|
//Synchronize Device
|
||||||
if (false == _is_mem_ready()) {
|
if (false == _is_mem_ready()) {
|
||||||
tr_error("ERROR: init - _is_mem_ready Failed");
|
tr_error("init - _is_mem_ready Failed");
|
||||||
status = SPIF_BD_ERROR_READY_FAILED;
|
status = SPIF_BD_ERROR_READY_FAILED;
|
||||||
goto exit_point;
|
goto exit_point;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************** Parse SFDP Header ***********************************/
|
/**************************** Parse SFDP Header ***********************************/
|
||||||
if (0 != _sfdp_parse_sfdp_headers(basic_table_addr, basic_table_size, sector_map_table_addr, sector_map_table_size)) {
|
if (0 != _sfdp_parse_sfdp_headers(basic_table_addr, basic_table_size, sector_map_table_addr, sector_map_table_size)) {
|
||||||
tr_error("ERROR: init - Parse SFDP Headers Failed");
|
tr_error("init - Parse SFDP Headers Failed");
|
||||||
status = SPIF_BD_ERROR_PARSING_FAILED;
|
status = SPIF_BD_ERROR_PARSING_FAILED;
|
||||||
goto exit_point;
|
goto exit_point;
|
||||||
}
|
}
|
||||||
|
@ -191,7 +191,7 @@ int SPIFBlockDevice::init()
|
||||||
|
|
||||||
/**************************** Parse Basic Parameters Table ***********************************/
|
/**************************** Parse Basic Parameters Table ***********************************/
|
||||||
if (0 != _sfdp_parse_basic_param_table(basic_table_addr, basic_table_size)) {
|
if (0 != _sfdp_parse_basic_param_table(basic_table_addr, basic_table_size)) {
|
||||||
tr_error("ERROR: init - Parse Basic Param Table Failed");
|
tr_error("init - Parse Basic Param Table Failed");
|
||||||
status = SPIF_BD_ERROR_PARSING_FAILED;
|
status = SPIF_BD_ERROR_PARSING_FAILED;
|
||||||
goto exit_point;
|
goto exit_point;
|
||||||
}
|
}
|
||||||
|
@ -202,10 +202,8 @@ int SPIFBlockDevice::init()
|
||||||
_region_high_boundary[0] = _device_size_bytes - 1;
|
_region_high_boundary[0] = _device_size_bytes - 1;
|
||||||
|
|
||||||
if ((sector_map_table_addr != 0) && (0 != sector_map_table_size)) {
|
if ((sector_map_table_addr != 0) && (0 != sector_map_table_size)) {
|
||||||
tr_info("INFO: init - Parsing Sector Map Table - addr: 0x%lxh, Size: %d", sector_map_table_addr,
|
|
||||||
sector_map_table_size);
|
|
||||||
if (0 != _sfdp_parse_sector_map_table(sector_map_table_addr, sector_map_table_size)) {
|
if (0 != _sfdp_parse_sector_map_table(sector_map_table_addr, sector_map_table_size)) {
|
||||||
tr_error("ERROR: init - Parse Sector Map Table Failed");
|
tr_error("init - Parse Sector Map Table Failed");
|
||||||
status = SPIF_BD_ERROR_PARSING_FAILED;
|
status = SPIF_BD_ERROR_PARSING_FAILED;
|
||||||
goto exit_point;
|
goto exit_point;
|
||||||
}
|
}
|
||||||
|
@ -215,6 +213,13 @@ int SPIFBlockDevice::init()
|
||||||
// Dummy And Mode Cycles Back default 0
|
// Dummy And Mode Cycles Back default 0
|
||||||
_dummy_and_mode_cycles = _write_dummy_and_mode_cycles;
|
_dummy_and_mode_cycles = _write_dummy_and_mode_cycles;
|
||||||
_is_initialized = true;
|
_is_initialized = true;
|
||||||
|
tr_debug("Device size: %llu Kbytes", _device_size_bytes / 1024);
|
||||||
|
|
||||||
|
if (_device_size_bytes > (1 << 24)) {
|
||||||
|
tr_debug("Size is bigger than 16MB and thus address does not fit in 3 byte, switch to 4 byte address mode");
|
||||||
|
_spi_send_general_command(SPIF_4BEN, SPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0);
|
||||||
|
_address_size = SPIF_ADDR_SIZE_4_BYTES;
|
||||||
|
}
|
||||||
|
|
||||||
exit_point:
|
exit_point:
|
||||||
_mutex->unlock();
|
_mutex->unlock();
|
||||||
|
@ -243,7 +248,7 @@ int SPIFBlockDevice::deinit()
|
||||||
// Disable Device for Writing
|
// Disable Device for Writing
|
||||||
status = _spi_send_general_command(SPIF_WRDI, SPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0);
|
status = _spi_send_general_command(SPIF_WRDI, SPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0);
|
||||||
if (status != SPIF_BD_ERROR_OK) {
|
if (status != SPIF_BD_ERROR_OK) {
|
||||||
tr_error("ERROR: Write Disable failed");
|
tr_error("Write Disable failed");
|
||||||
}
|
}
|
||||||
_is_initialized = false;
|
_is_initialized = false;
|
||||||
|
|
||||||
|
@ -260,7 +265,6 @@ int SPIFBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
|
||||||
}
|
}
|
||||||
|
|
||||||
int status = SPIF_BD_ERROR_OK;
|
int status = SPIF_BD_ERROR_OK;
|
||||||
tr_info("INFO Read - Inst: 0x%xh", _read_instruction);
|
|
||||||
_mutex->lock();
|
_mutex->lock();
|
||||||
|
|
||||||
// Set Dummy Cycles for Specific Read Command Mode
|
// Set Dummy Cycles for Specific Read Command Mode
|
||||||
|
@ -286,8 +290,6 @@ int SPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size)
|
||||||
uint32_t offset = 0;
|
uint32_t offset = 0;
|
||||||
uint32_t chunk = 0;
|
uint32_t chunk = 0;
|
||||||
|
|
||||||
tr_debug("DEBUG: program - Buff: 0x%lxh, addr: %llu, size: %llu", (uint32_t)buffer, addr, size);
|
|
||||||
|
|
||||||
while (size > 0) {
|
while (size > 0) {
|
||||||
|
|
||||||
// Write on _page_size_bytes boundaries (Default 256 bytes a page)
|
// Write on _page_size_bytes boundaries (Default 256 bytes a page)
|
||||||
|
@ -298,7 +300,7 @@ int SPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size)
|
||||||
|
|
||||||
//Send WREN
|
//Send WREN
|
||||||
if (_set_write_enable() != 0) {
|
if (_set_write_enable() != 0) {
|
||||||
tr_error("ERROR: Write Enabe failed\n");
|
tr_error("Write Enabe failed");
|
||||||
program_failed = true;
|
program_failed = true;
|
||||||
status = SPIF_BD_ERROR_WREN_FAILED;
|
status = SPIF_BD_ERROR_WREN_FAILED;
|
||||||
goto exit_point;
|
goto exit_point;
|
||||||
|
@ -311,7 +313,7 @@ int SPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size)
|
||||||
size -= chunk;
|
size -= chunk;
|
||||||
|
|
||||||
if (false == _is_mem_ready()) {
|
if (false == _is_mem_ready()) {
|
||||||
tr_error("ERROR: Device not ready after write, failed\n");
|
tr_error("Device not ready after write, failed");
|
||||||
program_failed = true;
|
program_failed = true;
|
||||||
status = SPIF_BD_ERROR_READY_FAILED;
|
status = SPIF_BD_ERROR_READY_FAILED;
|
||||||
goto exit_point;
|
goto exit_point;
|
||||||
|
@ -345,15 +347,13 @@ int SPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size)
|
||||||
// Erase Types of selected region
|
// Erase Types of selected region
|
||||||
uint8_t bitfield = _region_erase_types_bitfield[region];
|
uint8_t bitfield = _region_erase_types_bitfield[region];
|
||||||
|
|
||||||
tr_info("DEBUG: erase - addr: %llu, in_size: %llu", addr, in_size);
|
|
||||||
|
|
||||||
if ((addr + in_size) > _device_size_bytes) {
|
if ((addr + in_size) > _device_size_bytes) {
|
||||||
tr_error("ERROR: erase exceeds flash device size");
|
tr_error("erase exceeds flash device size");
|
||||||
return SPIF_BD_ERROR_INVALID_ERASE_PARAMS;
|
return SPIF_BD_ERROR_INVALID_ERASE_PARAMS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((addr % get_erase_size(addr)) != 0) || (((addr + in_size) % get_erase_size(addr + in_size - 1)) != 0)) {
|
if (((addr % get_erase_size(addr)) != 0) || (((addr + in_size) % get_erase_size(addr + in_size - 1)) != 0)) {
|
||||||
tr_error("ERROR: invalid erase - unaligned address and size");
|
tr_error("invalid erase - unaligned address and size");
|
||||||
return SPIF_BD_ERROR_INVALID_ERASE_PARAMS;
|
return SPIF_BD_ERROR_INVALID_ERASE_PARAMS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,15 +367,10 @@ int SPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size)
|
||||||
offset = addr % _erase_type_size_arr[type];
|
offset = addr % _erase_type_size_arr[type];
|
||||||
chunk = ((offset + size) < _erase_type_size_arr[type]) ? size : (_erase_type_size_arr[type] - offset);
|
chunk = ((offset + size) < _erase_type_size_arr[type]) ? size : (_erase_type_size_arr[type] - offset);
|
||||||
|
|
||||||
tr_debug("DEBUG: erase - addr: %llu, size:%d, Inst: 0x%xh, chunk: %lu , ",
|
|
||||||
addr, size, cur_erase_inst, chunk);
|
|
||||||
tr_debug("DEBUG: erase - Region: %d, Type:%d",
|
|
||||||
region, type);
|
|
||||||
|
|
||||||
_mutex->lock();
|
_mutex->lock();
|
||||||
|
|
||||||
if (_set_write_enable() != 0) {
|
if (_set_write_enable() != 0) {
|
||||||
tr_error("ERROR: SPI Erase Device not ready - failed");
|
tr_error("SPI Erase Device not ready - failed");
|
||||||
erase_failed = true;
|
erase_failed = true;
|
||||||
status = SPIF_BD_ERROR_READY_FAILED;
|
status = SPIF_BD_ERROR_READY_FAILED;
|
||||||
goto exit_point;
|
goto exit_point;
|
||||||
|
@ -393,7 +388,7 @@ int SPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (false == _is_mem_ready()) {
|
if (false == _is_mem_ready()) {
|
||||||
tr_error("ERROR: SPI After Erase Device not ready - failed\n");
|
tr_error("SPI After Erase Device not ready - failed");
|
||||||
erase_failed = true;
|
erase_failed = true;
|
||||||
status = SPIF_BD_ERROR_READY_FAILED;
|
status = SPIF_BD_ERROR_READY_FAILED;
|
||||||
goto exit_point;
|
goto exit_point;
|
||||||
|
@ -452,7 +447,7 @@ bd_size_t SPIFBlockDevice::get_erase_size(bd_addr_t addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i_ind == 4) {
|
if (i_ind == 4) {
|
||||||
tr_error("ERROR: no erase type was found for region addr");
|
tr_error("no erase type was found for region addr");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -555,8 +550,7 @@ spif_bd_error SPIFBlockDevice::_spi_send_program_command(int prog_inst, const vo
|
||||||
|
|
||||||
spif_bd_error SPIFBlockDevice::_spi_send_erase_command(int erase_inst, bd_addr_t addr, bd_size_t size)
|
spif_bd_error SPIFBlockDevice::_spi_send_erase_command(int erase_inst, bd_addr_t addr, bd_size_t size)
|
||||||
{
|
{
|
||||||
tr_info("INFO: Erase Inst: 0x%xh, addr: %llu, size: %llu", erase_inst, addr, size);
|
addr = (((int)addr) & 0xFFFFF000);
|
||||||
addr = (((int)addr) & 0x00FFF000);
|
|
||||||
_spi_send_general_command(erase_inst, addr, NULL, 0, NULL, 0);
|
_spi_send_general_command(erase_inst, addr, NULL, 0, NULL, 0);
|
||||||
return SPIF_BD_ERROR_OK;
|
return SPIF_BD_ERROR_OK;
|
||||||
}
|
}
|
||||||
|
@ -612,19 +606,19 @@ int SPIFBlockDevice::_sfdp_parse_sector_map_table(uint32_t sector_map_table_addr
|
||||||
spif_bd_error status = _spi_send_read_command(SPIF_SFDP, sector_map_table, sector_map_table_addr /*address*/,
|
spif_bd_error status = _spi_send_read_command(SPIF_SFDP, sector_map_table, sector_map_table_addr /*address*/,
|
||||||
sector_map_table_size);
|
sector_map_table_size);
|
||||||
if (status != SPIF_BD_ERROR_OK) {
|
if (status != SPIF_BD_ERROR_OK) {
|
||||||
tr_error("ERROR: init - Read SFDP First Table Failed");
|
tr_error("init - Read SFDP First Table Failed");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Currently we support only Single Map Descriptor
|
// Currently we support only Single Map Descriptor
|
||||||
if (!((sector_map_table[0] & 0x3) == 0x03) && (sector_map_table[1] == 0x0)) {
|
if (!((sector_map_table[0] & 0x3) == 0x03) && (sector_map_table[1] == 0x0)) {
|
||||||
tr_error("ERROR: Sector Map - Supporting Only Single! Map Descriptor (not map commands)");
|
tr_error("Sector Map - Supporting Only Single! Map Descriptor (not map commands)");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
_regions_count = sector_map_table[2] + 1;
|
_regions_count = sector_map_table[2] + 1;
|
||||||
if (_regions_count > SPIF_MAX_REGIONS) {
|
if (_regions_count > SPIF_MAX_REGIONS) {
|
||||||
tr_error("ERROR: Supporting up to %d regions, current setup to %d regions - fail",
|
tr_error("Supporting up to %d regions, current setup to %d regions - fail",
|
||||||
SPIF_MAX_REGIONS, _regions_count);
|
SPIF_MAX_REGIONS, _regions_count);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -666,13 +660,13 @@ int SPIFBlockDevice::_sfdp_parse_basic_param_table(uint32_t basic_table_addr, si
|
||||||
spif_bd_error status = _spi_send_read_command(SPIF_SFDP, param_table, basic_table_addr /*address*/,
|
spif_bd_error status = _spi_send_read_command(SPIF_SFDP, param_table, basic_table_addr /*address*/,
|
||||||
basic_table_size);
|
basic_table_size);
|
||||||
if (status != SPIF_BD_ERROR_OK) {
|
if (status != SPIF_BD_ERROR_OK) {
|
||||||
tr_error("ERROR: init - Read SFDP First Table Failed");
|
tr_error("init - Read SFDP First Table Failed");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check address size, currently only supports 3byte addresses
|
// Check address size, currently only supports 3byte addresses
|
||||||
if ((param_table[2] & 0x4) != 0 || (param_table[7] & 0x80) != 0) {
|
if ((param_table[2] & 0x4) != 0 || (param_table[7] & 0x80) != 0) {
|
||||||
tr_error("ERROR: init - verify 3byte addressing Failed");
|
tr_error("init - verify 3byte addressing Failed");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -683,6 +677,7 @@ int SPIFBlockDevice::_sfdp_parse_basic_param_table(uint32_t basic_table_addr, si
|
||||||
(param_table[5] << 8) |
|
(param_table[5] << 8) |
|
||||||
param_table[4]);
|
param_table[4]);
|
||||||
_device_size_bytes = (density_bits + 1) / 8;
|
_device_size_bytes = (density_bits + 1) / 8;
|
||||||
|
tr_debug("Density bits: %ld , device size: %llu bytes", density_bits, _device_size_bytes);
|
||||||
|
|
||||||
// Set Default read/program/erase Instructions
|
// Set Default read/program/erase Instructions
|
||||||
_read_instruction = SPIF_READ;
|
_read_instruction = SPIF_READ;
|
||||||
|
@ -718,23 +713,19 @@ int SPIFBlockDevice::_sfdp_parse_sfdp_headers(uint32_t &basic_table_addr, size_t
|
||||||
|
|
||||||
spif_bd_error status = _spi_send_read_command(SPIF_SFDP, sfdp_header, addr /*address*/, data_length);
|
spif_bd_error status = _spi_send_read_command(SPIF_SFDP, sfdp_header, addr /*address*/, data_length);
|
||||||
if (status != SPIF_BD_ERROR_OK) {
|
if (status != SPIF_BD_ERROR_OK) {
|
||||||
tr_error("ERROR: init - Read SFDP Failed");
|
tr_error("init - Read SFDP Failed");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify SFDP signature for sanity
|
// Verify SFDP signature for sanity
|
||||||
// Also check that major/minor version is acceptable
|
// Also check that major/minor version is acceptable
|
||||||
if (!(memcmp(&sfdp_header[0], "SFDP", 4) == 0 && sfdp_header[5] == 1)) {
|
if (!(memcmp(&sfdp_header[0], "SFDP", 4) == 0 && sfdp_header[5] == 1)) {
|
||||||
tr_error("ERROR: init - _verify SFDP signature and version Failed");
|
tr_error("init - _verify SFDP signature and version Failed");
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
|
||||||
tr_info("INFO: init - verified SFDP Signature and version Successfully");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discover Number of Parameter Headers
|
// Discover Number of Parameter Headers
|
||||||
int number_of_param_headers = (int)(sfdp_header[6]) + 1;
|
int number_of_param_headers = (int)(sfdp_header[6]) + 1;
|
||||||
tr_debug("DEBUG: number of Param Headers: %d", number_of_param_headers);
|
|
||||||
|
|
||||||
|
|
||||||
addr += SPIF_SFDP_HEADER_SIZE;
|
addr += SPIF_SFDP_HEADER_SIZE;
|
||||||
data_length = SPIF_PARAM_HEADER_SIZE;
|
data_length = SPIF_PARAM_HEADER_SIZE;
|
||||||
|
@ -744,27 +735,25 @@ int SPIFBlockDevice::_sfdp_parse_sfdp_headers(uint32_t &basic_table_addr, size_t
|
||||||
|
|
||||||
status = _spi_send_read_command(SPIF_SFDP, param_header, addr, data_length);
|
status = _spi_send_read_command(SPIF_SFDP, param_header, addr, data_length);
|
||||||
if (status != SPIF_BD_ERROR_OK) {
|
if (status != SPIF_BD_ERROR_OK) {
|
||||||
tr_error("ERROR: init - Read Param Table %d Failed", i_ind + 1);
|
tr_error("init - Read Param Table %d Failed", i_ind + 1);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The SFDP spec indicates the standard table is always at offset 0
|
// The SFDP spec indicates the standard table is always at offset 0
|
||||||
// in the parameter headers, we check just to be safe
|
// in the parameter headers, we check just to be safe
|
||||||
if (param_header[2] != 1) {
|
if (param_header[2] != 1) {
|
||||||
tr_error("ERROR: Param Table %d - Major Version should be 1!", i_ind + 1);
|
tr_error("Param Table %d - Major Version should be 1!", i_ind + 1);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((param_header[0] == 0) && (param_header[7] == 0xFF)) {
|
if ((param_header[0] == 0) && (param_header[7] == 0xFF)) {
|
||||||
// Found Basic Params Table: LSB=0x00, MSB=0xFF
|
// Found Basic Params Table: LSB=0x00, MSB=0xFF
|
||||||
tr_debug("DEBUG: Found Basic Param Table at Table: %d", i_ind + 1);
|
|
||||||
basic_table_addr = ((param_header[6] << 16) | (param_header[5] << 8) | (param_header[4]));
|
basic_table_addr = ((param_header[6] << 16) | (param_header[5] << 8) | (param_header[4]));
|
||||||
// Supporting up to 64 Bytes Table (16 DWORDS)
|
// Supporting up to 64 Bytes Table (16 DWORDS)
|
||||||
basic_table_size = ((param_header[3] * 4) < SFDP_DEFAULT_BASIC_PARAMS_TABLE_SIZE_BYTES) ? (param_header[3] * 4) : 64;
|
basic_table_size = ((param_header[3] * 4) < SFDP_DEFAULT_BASIC_PARAMS_TABLE_SIZE_BYTES) ? (param_header[3] * 4) : 64;
|
||||||
|
|
||||||
} else if ((param_header[0] == 81) && (param_header[7] == 0xFF)) {
|
} else if ((param_header[0] == 81) && (param_header[7] == 0xFF)) {
|
||||||
// Found Sector Map Table: LSB=0x81, MSB=0xFF
|
// Found Sector Map Table: LSB=0x81, MSB=0xFF
|
||||||
tr_debug("DEBUG: Found Sector Map Table at Table: %d", i_ind + 1);
|
|
||||||
sector_map_table_addr = ((param_header[6] << 16) | (param_header[5] << 8) | (param_header[4]));
|
sector_map_table_addr = ((param_header[6] << 16) | (param_header[5] << 8) | (param_header[4]));
|
||||||
sector_map_table_size = param_header[3] * 4;
|
sector_map_table_size = param_header[3] * 4;
|
||||||
|
|
||||||
|
@ -783,9 +772,6 @@ unsigned int SPIFBlockDevice::_sfdp_detect_page_size(uint8_t *basic_param_table_
|
||||||
// Page Size is specified by 4 Bits (N), calculated by 2^N
|
// Page Size is specified by 4 Bits (N), calculated by 2^N
|
||||||
int page_to_power_size = ((int)basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_PAGE_SIZE_BYTE]) >> 4;
|
int page_to_power_size = ((int)basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_PAGE_SIZE_BYTE]) >> 4;
|
||||||
page_size = local_math_power(2, page_to_power_size);
|
page_size = local_math_power(2, page_to_power_size);
|
||||||
tr_debug("DEBUG: Detected Page Size: %d", page_size);
|
|
||||||
} else {
|
|
||||||
tr_debug("DEBUG: Using Default Page Size: %d", page_size);
|
|
||||||
}
|
}
|
||||||
return page_size;
|
return page_size;
|
||||||
}
|
}
|
||||||
|
@ -807,8 +793,6 @@ int SPIFBlockDevice::_sfdp_detect_erase_types_inst_and_size(uint8_t *basic_param
|
||||||
erase_type_inst_arr[i_ind] = 0xff; //0xFF default for unsupported type
|
erase_type_inst_arr[i_ind] = 0xff; //0xFF default for unsupported type
|
||||||
erase_type_size_arr[i_ind] = local_math_power(2,
|
erase_type_size_arr[i_ind] = local_math_power(2,
|
||||||
basic_param_table_ptr[SPIF_BASIC_PARAM_ERASE_TYPE_1_SIZE_BYTE + 2 * i_ind]); // Size given as 2^N
|
basic_param_table_ptr[SPIF_BASIC_PARAM_ERASE_TYPE_1_SIZE_BYTE + 2 * i_ind]); // Size given as 2^N
|
||||||
tr_info("DEBUG: Erase Type(A) %d - Inst: 0x%xh, Size: %d", (i_ind + 1), erase_type_inst_arr[i_ind],
|
|
||||||
erase_type_size_arr[i_ind]);
|
|
||||||
if (erase_type_size_arr[i_ind] > 1) {
|
if (erase_type_size_arr[i_ind] > 1) {
|
||||||
// if size==1 type is not supported
|
// if size==1 type is not supported
|
||||||
erase_type_inst_arr[i_ind] = basic_param_table_ptr[SPIF_BASIC_PARAM_ERASE_TYPE_1_BYTE + 2 * i_ind];
|
erase_type_inst_arr[i_ind] = basic_param_table_ptr[SPIF_BASIC_PARAM_ERASE_TYPE_1_BYTE + 2 * i_ind];
|
||||||
|
@ -824,21 +808,19 @@ int SPIFBlockDevice::_sfdp_detect_erase_types_inst_and_size(uint8_t *basic_param
|
||||||
if (erase4k_inst != erase_type_inst_arr[i_ind]) {
|
if (erase4k_inst != erase_type_inst_arr[i_ind]) {
|
||||||
//Verify 4KErase Type is identical to Legacy 4K erase type specified in Byte 1 of Param Table
|
//Verify 4KErase Type is identical to Legacy 4K erase type specified in Byte 1 of Param Table
|
||||||
erase4k_inst = erase_type_inst_arr[i_ind];
|
erase4k_inst = erase_type_inst_arr[i_ind];
|
||||||
tr_warning("WARNING: _detectEraseTypesInstAndSize - Default 4K erase Inst is different than erase type Inst for 4K");
|
tr_warning("_detectEraseTypesInstAndSize - Default 4K erase Inst is different than erase type Inst for 4K");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_region_erase_types_bitfield[0] |= bitfield; // If there's no region map, set region "0" types bitfield as defualt;
|
_region_erase_types_bitfield[0] |= bitfield; // If there's no region map, set region "0" types bitfield as defualt;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr_info("INFO: Erase Type %d - Inst: 0x%xh, Size: %d", (i_ind + 1), erase_type_inst_arr[i_ind],
|
|
||||||
erase_type_size_arr[i_ind]);
|
|
||||||
bitfield = bitfield << 1;
|
bitfield = bitfield << 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (false == found_4Kerase_type) {
|
if (false == found_4Kerase_type) {
|
||||||
tr_warning("WARNING: Couldn't find Erase Type for 4KB size");
|
tr_warning("Couldn't find Erase Type for 4KB size");
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -859,7 +841,6 @@ int SPIFBlockDevice::_sfdp_detect_best_bus_read_mode(uint8_t *basic_param_table_
|
||||||
read_inst = basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE];
|
read_inst = basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE];
|
||||||
_read_dummy_and_mode_cycles = (basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE - 1] >> 5)
|
_read_dummy_and_mode_cycles = (basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE - 1] >> 5)
|
||||||
+ (basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE - 1] & 0x1F);
|
+ (basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE - 1] & 0x1F);
|
||||||
tr_info("\nDEBUG: Read Bus Mode set to 2-2-2, Instruction: 0x%xh", read_inst);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -869,7 +850,6 @@ int SPIFBlockDevice::_sfdp_detect_best_bus_read_mode(uint8_t *basic_param_table_
|
||||||
read_inst = basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE];
|
read_inst = basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE];
|
||||||
_read_dummy_and_mode_cycles = (basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE - 1] >> 5)
|
_read_dummy_and_mode_cycles = (basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE - 1] >> 5)
|
||||||
+ (basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE - 1] & 0x1F);
|
+ (basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE - 1] & 0x1F);
|
||||||
tr_debug("\nDEBUG: Read Bus Mode set to 1-2-2, Instruction: 0x%xh", read_inst);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (examined_byte & 0x01) {
|
if (examined_byte & 0x01) {
|
||||||
|
@ -877,12 +857,10 @@ int SPIFBlockDevice::_sfdp_detect_best_bus_read_mode(uint8_t *basic_param_table_
|
||||||
read_inst = basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE];
|
read_inst = basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE];
|
||||||
_read_dummy_and_mode_cycles = (basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE - 1] >> 5)
|
_read_dummy_and_mode_cycles = (basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE - 1] >> 5)
|
||||||
+ (basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE - 1] & 0x1F);
|
+ (basic_param_table_ptr[SPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE - 1] & 0x1F);
|
||||||
tr_debug("\nDEBUG: Read Bus Mode set to 1-1-2, Instruction: 0x%xh", _read_instruction);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
_read_dummy_and_mode_cycles = 0;
|
_read_dummy_and_mode_cycles = 0;
|
||||||
tr_debug("\nDEBUG: Read Bus Mode set to 1-1-1, Instruction: 0x%xh", read_inst);
|
|
||||||
} while (false);
|
} while (false);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -893,13 +871,10 @@ int SPIFBlockDevice::_reset_flash_mem()
|
||||||
// Perform Soft Reset of the Device prior to initialization
|
// Perform Soft Reset of the Device prior to initialization
|
||||||
int status = 0;
|
int status = 0;
|
||||||
char status_value[2] = {0};
|
char status_value[2] = {0};
|
||||||
tr_info("INFO: _reset_flash_mem:\n");
|
|
||||||
//Read the Status Register from device
|
//Read the Status Register from device
|
||||||
if (SPIF_BD_ERROR_OK == _spi_send_general_command(SPIF_RDSR, SPI_NO_ADDRESS_COMMAND, NULL, 0, status_value, 1)) {
|
if (SPIF_BD_ERROR_OK == _spi_send_general_command(SPIF_RDSR, SPI_NO_ADDRESS_COMMAND, NULL, 0, status_value, 1)) {
|
||||||
// store received values in status_value
|
// store received values in status_value
|
||||||
tr_debug("DEBUG: Reading Status Register Success: value = 0x%x\n", (int)status_value[0]);
|
|
||||||
} else {
|
} else {
|
||||||
tr_debug("ERROR: Reading Status Register failed\n");
|
|
||||||
status = -1;
|
status = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -907,9 +882,8 @@ int SPIFBlockDevice::_reset_flash_mem()
|
||||||
//Send Reset Enable
|
//Send Reset Enable
|
||||||
if (SPIF_BD_ERROR_OK == _spi_send_general_command(SPIF_RSTEN, SPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0)) {
|
if (SPIF_BD_ERROR_OK == _spi_send_general_command(SPIF_RSTEN, SPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0)) {
|
||||||
// store received values in status_value
|
// store received values in status_value
|
||||||
tr_debug("DEBUG: Sending RSTEN Success\n");
|
|
||||||
} else {
|
} else {
|
||||||
tr_error("ERROR: Sending RSTEN failed\n");
|
tr_error("Sending RSTEN failed");
|
||||||
status = -1;
|
status = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -917,9 +891,8 @@ int SPIFBlockDevice::_reset_flash_mem()
|
||||||
//Send Reset
|
//Send Reset
|
||||||
if (SPIF_BD_ERROR_OK == _spi_send_general_command(SPIF_RST, SPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0)) {
|
if (SPIF_BD_ERROR_OK == _spi_send_general_command(SPIF_RST, SPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0)) {
|
||||||
// store received values in status_value
|
// store received values in status_value
|
||||||
tr_debug("DEBUG: Sending RST Success\n");
|
|
||||||
} else {
|
} else {
|
||||||
tr_error("ERROR: Sending RST failed\n");
|
tr_error("Sending RST failed");
|
||||||
status = -1;
|
status = -1;
|
||||||
}
|
}
|
||||||
_is_mem_ready();
|
_is_mem_ready();
|
||||||
|
@ -942,12 +915,12 @@ bool SPIFBlockDevice::_is_mem_ready()
|
||||||
//Read the Status Register from device
|
//Read the Status Register from device
|
||||||
if (SPIF_BD_ERROR_OK != _spi_send_general_command(SPIF_RDSR, SPI_NO_ADDRESS_COMMAND, NULL, 0, status_value,
|
if (SPIF_BD_ERROR_OK != _spi_send_general_command(SPIF_RDSR, SPI_NO_ADDRESS_COMMAND, NULL, 0, status_value,
|
||||||
1)) { // store received values in status_value
|
1)) { // store received values in status_value
|
||||||
tr_error("ERROR: Reading Status Register failed\n");
|
tr_error("Reading Status Register failed");
|
||||||
}
|
}
|
||||||
} while ((status_value[0] & SPIF_STATUS_BIT_WIP) != 0 && retries < IS_MEM_READY_MAX_RETRIES);
|
} while ((status_value[0] & SPIF_STATUS_BIT_WIP) != 0 && retries < IS_MEM_READY_MAX_RETRIES);
|
||||||
|
|
||||||
if ((status_value[0] & SPIF_STATUS_BIT_WIP) != 0) {
|
if ((status_value[0] & SPIF_STATUS_BIT_WIP) != 0) {
|
||||||
tr_error("ERROR: _is_mem_ready FALSE\n");
|
tr_error("_is_mem_ready FALSE");
|
||||||
mem_ready = false;
|
mem_ready = false;
|
||||||
}
|
}
|
||||||
return mem_ready;
|
return mem_ready;
|
||||||
|
@ -961,24 +934,24 @@ int SPIFBlockDevice::_set_write_enable()
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (SPIF_BD_ERROR_OK != _spi_send_general_command(SPIF_WREN, SPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0)) {
|
if (SPIF_BD_ERROR_OK != _spi_send_general_command(SPIF_WREN, SPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0)) {
|
||||||
tr_error("ERROR:Sending WREN command FAILED\n");
|
tr_error("Sending WREN command FAILED");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (false == _is_mem_ready()) {
|
if (false == _is_mem_ready()) {
|
||||||
tr_error("ERROR: Device not ready, write failed");
|
tr_error("Device not ready, write failed");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(status_value, 0, 2);
|
memset(status_value, 0, 2);
|
||||||
if (SPIF_BD_ERROR_OK != _spi_send_general_command(SPIF_RDSR, SPI_NO_ADDRESS_COMMAND, NULL, 0, status_value,
|
if (SPIF_BD_ERROR_OK != _spi_send_general_command(SPIF_RDSR, SPI_NO_ADDRESS_COMMAND, NULL, 0, status_value,
|
||||||
1)) { // store received values in status_value
|
1)) { // store received values in status_value
|
||||||
tr_error("ERROR: Reading Status Register failed\n");
|
tr_error("Reading Status Register failed");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((status_value[0] & SPIF_STATUS_BIT_WEL) == 0) {
|
if ((status_value[0] & SPIF_STATUS_BIT_WEL) == 0) {
|
||||||
tr_error("ERROR: _set_write_enable failed\n");
|
tr_error("_set_write_enable failed");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
status = 0;
|
status = 0;
|
||||||
|
@ -1031,7 +1004,7 @@ int SPIFBlockDevice::_utils_iterate_next_largest_erase_type(uint8_t &bitfield, i
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i_ind == 4) {
|
if (i_ind == 4) {
|
||||||
tr_error("ERROR: no erase type was found for current region addr");
|
tr_error("no erase type was found for current region addr");
|
||||||
}
|
}
|
||||||
return largest_erase_type;
|
return largest_erase_type;
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,14 @@ uint32_t I2CTester::get_receive_checksum()
|
||||||
return to_slave_checksum;
|
return to_slave_checksum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t I2CTester::get_send_checksum()
|
||||||
|
{
|
||||||
|
uint32_t from_slave_checksum = 0;
|
||||||
|
MBED_ASSERT(sizeof(from_slave_checksum) == TESTER_I2C_FROM_SLAVE_CHECKSUM_SIZE);
|
||||||
|
read(TESTER_I2C_FROM_SLAVE_CHECKSUM, (uint8_t *)&from_slave_checksum, sizeof(from_slave_checksum));
|
||||||
|
return from_slave_checksum;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t I2CTester::state_num()
|
uint8_t I2CTester::state_num()
|
||||||
{
|
{
|
||||||
uint8_t state_num = 0;
|
uint8_t state_num = 0;
|
||||||
|
@ -71,9 +79,16 @@ uint8_t I2CTester::state_num()
|
||||||
|
|
||||||
uint8_t I2CTester::num_dev_addr_matches()
|
uint8_t I2CTester::num_dev_addr_matches()
|
||||||
{
|
{
|
||||||
uint8_t num_correct = 0;
|
uint8_t num_dev_addr_matches = 0;
|
||||||
read(TESTER_I2C_NUMBER_DEV_ADDR_MATCHES, &num_correct, sizeof(num_correct));
|
read(TESTER_I2C_NUMBER_DEV_ADDR_MATCHES, &num_dev_addr_matches, sizeof(num_dev_addr_matches));
|
||||||
return num_correct;
|
return num_dev_addr_matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t I2CTester::num_dev_addr_mismatches()
|
||||||
|
{
|
||||||
|
uint8_t num_dev_addr_mismatches = 0;
|
||||||
|
read(TESTER_I2C_NUMBER_DEV_ADDR_MISMATCHES, &num_dev_addr_mismatches, sizeof(num_dev_addr_mismatches));
|
||||||
|
return num_dev_addr_mismatches;
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2CTester::set_device_address(uint16_t addr)
|
void I2CTester::set_device_address(uint16_t addr)
|
||||||
|
|
|
@ -61,10 +61,10 @@ public:
|
||||||
uint16_t num_nacks();
|
uint16_t num_nacks();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the number of transfers which have occurred, not including the device address byte
|
* Read the number of transfers which have occurred
|
||||||
*
|
*
|
||||||
* @return The number of I2C transfers that have completed since
|
* @return The number of I2C transfers that have completed since
|
||||||
* i2c was reset, not including the device address byte
|
* i2c was reset, including the device address byte.
|
||||||
*/
|
*/
|
||||||
uint16_t transfer_count();
|
uint16_t transfer_count();
|
||||||
|
|
||||||
|
@ -75,6 +75,13 @@ public:
|
||||||
*/
|
*/
|
||||||
uint32_t get_receive_checksum();
|
uint32_t get_receive_checksum();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a checksum the tester calculated for data it has already sent
|
||||||
|
*
|
||||||
|
* @return The sum of all bytes the tester has sent since reset
|
||||||
|
*/
|
||||||
|
uint32_t get_send_checksum();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the I2C slave state number
|
* Get the I2C slave state number
|
||||||
*
|
*
|
||||||
|
@ -89,6 +96,13 @@ public:
|
||||||
*/
|
*/
|
||||||
uint8_t num_dev_addr_matches();
|
uint8_t num_dev_addr_matches();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of times the device address has been sent incorrectly
|
||||||
|
*
|
||||||
|
* @return The number of times the device address has been sent incorrectly
|
||||||
|
*/
|
||||||
|
uint8_t num_dev_addr_mismatches();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the I2C slave device address
|
* Set the I2C slave device address
|
||||||
*
|
*
|
||||||
|
@ -153,7 +167,7 @@ public:
|
||||||
uint8_t get_next_from_slave();
|
uint8_t get_next_from_slave();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the number of writes which have occurred, not including the device address byte
|
* Read the number of writes which have occurred
|
||||||
*
|
*
|
||||||
* @return The number of I2C writes that have completed since
|
* @return The number of I2C writes that have completed since
|
||||||
* i2c was reset, not including the device address byte
|
* i2c was reset, not including the device address byte
|
||||||
|
@ -164,7 +178,7 @@ public:
|
||||||
* Read the number of reads which have occurred
|
* Read the number of reads which have occurred
|
||||||
*
|
*
|
||||||
* @return The number of I2C reads that have completed since
|
* @return The number of I2C reads that have completed since
|
||||||
* i2c was reset
|
* i2c was reset, not including the device address byte
|
||||||
*/
|
*/
|
||||||
uint16_t num_reads();
|
uint16_t num_reads();
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@
|
||||||
* // Map D6 to LogicalPinGPIO0
|
* // Map D6 to LogicalPinGPIO0
|
||||||
* tester.pin_map_set(D6, MbedTester::LogicalPinGPIO0);
|
* tester.pin_map_set(D6, MbedTester::LogicalPinGPIO0);
|
||||||
*
|
*
|
||||||
* // Toggle the LED
|
* // Toggle pin D6
|
||||||
* int toggle = 0;
|
* int toggle = 0;
|
||||||
* while (1) {
|
* while (1) {
|
||||||
* tester.gpio_write(MbedTester::LogicalPinGPIO0, toggle, true);
|
* tester.gpio_write(MbedTester::LogicalPinGPIO0, toggle, true);
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue