Add documentation and test the HAL RTC API

Add requirements, tests, an example implementation and additional
function documentation to the HAL RTC API.
pull/7009/head
Russ Butler 2017-08-11 14:27:24 -05:00 committed by Bartek Szatkowski
parent a0a9626420
commit 1f97f11373
6 changed files with 563 additions and 8 deletions

View File

@ -0,0 +1,119 @@
"""
mbed SDK
Copyright (c) 2017-2017 ARM Limited
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from __future__ import print_function
from mbed_host_tests import BaseHostTest
from time import sleep
class RtcResetTest(BaseHostTest):
"""This test checks that a device's RTC keeps count through a reset
It does this by setting the RTC's time, triggering a reset,
delaying and then reading the RTC's time again to ensure
that the RTC is still counting.
"""
"""Start of the RTC"""
START_TIME = 50000
START_TIME_TOLERANCE = 10
"""Time to delay after sending reset"""
DELAY_TIME = 4.0
DELAY_TOLERANCE = 1.0
VALUE_PLACEHOLDER = "0"
def setup(self):
"""Register callbacks required for the test"""
self._error = False
generator = self.rtc_reset_test()
generator.next()
def run_gen(key, value, time):
"""Run the generator, and fail testing if the iterator stops"""
if self._error:
return
try:
print("Calling generator")
generator.send((key, value, time))
except StopIteration:
self._error = True
for resp in ("start", "read"):
self.register_callback(resp, run_gen)
def teardown(self):
"""No work to do here"""
pass
def rtc_reset_test(self):
"""Generator for running the reset test
This function calls yield to wait for the next event from
the device. If the device gives the wrong response, then the
generator terminates by returing which raises a StopIteration
exception and fails the test.
"""
# Wait for start token
key, value, time = yield
if key != "start":
return
# Initialize, and set the time
self.send_kv("init", self.VALUE_PLACEHOLDER)
self.send_kv("write", str(self.START_TIME))
self.send_kv("read", self.VALUE_PLACEHOLDER)
key, value, time = yield
if key != "read":
return
dev_time_start = int(value)
# Unitialize, and reset
self.send_kv("free", self.VALUE_PLACEHOLDER)
self.send_kv("reset", self.VALUE_PLACEHOLDER)
sleep(self.DELAY_TIME)
# Restart the test, and send the sync token
self.send_kv("__sync", "00000000-0000-000000000-000000000000")
key, value, time = yield
if key != "start":
return
# Initialize, and read the time
self.send_kv("init", self.VALUE_PLACEHOLDER)
self.send_kv("read", self.VALUE_PLACEHOLDER)
key, value, time = yield
if key != "read":
return
dev_time_end = int(value)
# Check result
elapsed = dev_time_end - dev_time_start
start_time_valid = (self.START_TIME <= dev_time_start <
self.START_TIME + self.START_TIME_TOLERANCE)
elapsed_time_valid = elapsed >= self.DELAY_TIME - self.DELAY_TOLERANCE
passed = start_time_valid and elapsed_time_valid
if not start_time_valid:
self.log("FAIL: Expected start time of %i got %i" %
(self.START_TIME, dev_time_start))
elif not passed:
self.log("FAIL: Delayed for %fs but device "
"reported elapsed time of %fs" %
(self.DELAY_TIME, elapsed))
self.send_kv("exit", "pass" if passed else "fail")
yield # No more events expected

127
TESTS/mbed_hal/rtc/main.cpp Normal file
View File

@ -0,0 +1,127 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if !DEVICE_RTC
#error [NOT_SUPPORTED] RTC API not supported for this target
#endif
#include "utest/utest.h"
#include "unity/unity.h"
#include "greentea-client/test_env.h"
#include "rtc_test.h"
#include "mbed.h"
#include "rtc_api.h"
using namespace utest::v1;
static const uint32_t WAIT_TIME = 4;
static const uint32_t WAIT_TOLERANCE = 1;
void rtc_init_test()
{
for (int i = 0; i < 10; i++) {
rtc_init();
}
}
void rtc_sleep_test()
{
const uint32_t start = 100;
rtc_init();
rtc_write(start);
wait(WAIT_TIME);
const uint32_t stop = rtc_read();
rtc_free();
TEST_ASSERT_UINT32_WITHIN(WAIT_TOLERANCE, WAIT_TIME, stop - start);
}
void rtc_persist_test()
{
const uint32_t start = 100;
rtc_init();
rtc_write(start);
rtc_free();
wait(WAIT_TIME);
rtc_init();
const uint32_t stop = rtc_read();
const int enabled = rtc_isenabled();
rtc_free();
TEST_ASSERT_TRUE(enabled);
TEST_ASSERT_UINT32_WITHIN(WAIT_TOLERANCE, WAIT_TIME, stop - start);
}
void rtc_glitch_test()
{
const uint32_t start = 0xffffe;
rtc_init();
rtc_write(start);
uint32_t last = start;
while (last < start + 4) {
const uint32_t cur = rtc_read();
TEST_ASSERT(cur >= last);
last = cur;
}
rtc_free();
}
void rtc_range_test()
{
static const uint32_t starts[] = {
0x00000000,
0xEFFFFFFF,
0x00001000,
};
rtc_init();
for (uint32_t i = 0; i < sizeof(starts) / sizeof(starts[0]); i++) {
const uint32_t start = starts[i];
rtc_write(start);
wait(WAIT_TIME);
const uint32_t stop = rtc_read();
TEST_ASSERT_UINT32_WITHIN(WAIT_TOLERANCE, WAIT_TIME, stop - start);
}
rtc_free();
}
Case cases[] = {
Case("RTC - init", rtc_init_test),
Case("RTC - sleep", rtc_sleep_test),
Case("RTC - persist", rtc_persist_test),
Case("RTC - glitch", rtc_glitch_test),
Case("RTC - range", rtc_range_test),
};
utest::v1::status_t greentea_test_setup(const size_t number_of_cases) {
GREENTEA_SETUP(30, "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);
}

View File

@ -0,0 +1,63 @@
/** \addtogroup hal_rtc_tests
* @{
*/
/* mbed Microcontroller Library
* Copyright (c) 2017-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_RTC_TEST_H
#define MBED_RTC_TEST_H
#if DEVICE_RTC
#ifdef __cplusplus
extern "C" {
#endif
/** Test that ::rtc_init can be called multiple times
*
*/
void rtc_init_test(void);
/** Test that the RTC keeps counting in the various sleep modes
*
*/
void rtc_sleep_test(void);
/** Test that the RTC keeps counting even after ::rtc_free has been called
*
*/
void rtc_persist_test(void);
/** Test time does not glitch backwards due to an incorrectly implemented ripple counter driver
*
*/
void rtc_glitch_test(void);
/** Test that the RTC correctly handles large time values
*
*/
void rtc_range_test(void);
/**@}*/
#ifdef __cplusplus
}
#endif
#endif
#endif
/** @}*/

View File

@ -0,0 +1,98 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if !DEVICE_RTC
#error [NOT_SUPPORTED] RTC API not supported for this target
#endif
#include "utest/utest.h"
#include "unity/unity.h"
#include "greentea-client/test_env.h"
#include "rtc_reset_test.h"
#include "mbed.h"
#include "rtc_api.h"
typedef enum {
CMD_STATUS_PASS,
CMD_STATUS_FAIL,
CMD_STATUS_CONTINUE,
CMD_STATUS_ERROR
} cmd_status_t;
static cmd_status_t handle_command(const char *key, const char *value)
{
if (strcmp(key, "init") == 0) {
rtc_init();
return CMD_STATUS_CONTINUE;
} else if (strcmp(key, "free") == 0) {
rtc_free();
return CMD_STATUS_CONTINUE;
} else if (strcmp(key, "read") == 0) {
static char time_buf[64];
memset(time_buf, 0, sizeof(time_buf));
sprintf(time_buf, "%lu", (uint32_t)rtc_read());
greentea_send_kv("read", time_buf);
return CMD_STATUS_CONTINUE;
} else if (strcmp(key, "write") == 0) {
uint32_t time;
sscanf(value, "%lu", &time);
rtc_write(time);
return CMD_STATUS_CONTINUE;
} else if (strcmp(key, "reset") == 0) {
NVIC_SystemReset();
// Code shouldn't read this due to the reset
return CMD_STATUS_ERROR;
} else if (strcmp(key, "exit") == 0) {
return strcmp(value, "pass") == 0 ? CMD_STATUS_PASS : CMD_STATUS_FAIL;
} else {
return CMD_STATUS_ERROR;
}
}
void rtc_reset_test()
{
GREENTEA_SETUP(60, "rtc_reset");
static char _key[10 + 1] = {};
static char _value[128 + 1] = {};
greentea_send_kv("start", 1);
// Handshake with host
cmd_status_t cmd_status = CMD_STATUS_CONTINUE;
while (CMD_STATUS_CONTINUE == cmd_status) {
memset(_key, 0, sizeof(_key));
memset(_value, 0, sizeof(_value));
greentea_parse_kv(_key, _value, sizeof(_key) - 1, sizeof(_value) - 1);
cmd_status = handle_command(_key, _value);
}
GREENTEA_TESTSUITE_RESULT(CMD_STATUS_PASS == cmd_status);
}
int main()
{
rtc_reset_test();
}

View File

@ -0,0 +1,40 @@
/* mbed Microcontroller Library
* Copyright (c) 2017-2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_RTC_TEST_H
#define MBED_RTC_TEST_H
#if DEVICE_RTC
#ifdef __cplusplus
extern "C" {
#endif
/** Test that the RTC does not stop counting after a software reset
* \ingroup hal_rtc_tests
*/
void rtc_reset_test();
/**@}*/
#ifdef __cplusplus
}
#endif
#endif
#endif
/** @}*/

View File

@ -28,37 +28,145 @@ extern "C" {
#endif
/**
* \defgroup hal_rtc RTC hal functions
* \defgroup hal_rtc RTC hal
*
* The RTC hal provides a low level interface to the Real Time Counter (RTC) of a
* target.
*
* # Defined behavior
* * The function ::rtc_init is safe to call repeatedly - Verified by test ::rtc_init_test
* * RTC accuracy is at least 10% - Not verified
* * Init/free doesn't stop RTC from counting - Verified by test ::rtc_persist_test
* * Software reset doesn't stop RTC from counting - Verified by ::rtc_reset_test
* * Sleep modes don't stop RTC from counting - Verified by ::rtc_sleep_test
* * Shutdown mode doesn't stop RTC from counting - Not verified
*
* # Undefined behavior
* * Calling any function other than ::rtc_init before the initialization of the RTC
*
* # Potential bugs
* * Incorrect overflow handling - Verified by ::rtc_range_test
* * Glitches due to ripple counter - Verified by ::rtc_glitch_test
*
* @see hal_rtc_tests
*
* @{
*/
/**
* \defgroup hal_rtc_tests RTC hal tests
* The RTC test validate proper implementation of the RTC hal.
*
* To run the RTC hal tests use the command:
*
* mbed test -t <toolchain> -m <target> -n tests-mbed_hal-rtc*
*/
/** Initialize the RTC peripheral
*
* Powerup the RTC in perpetration for access. This function must be called
* before any other RTC functions ares called. This does not change the state
* of the RTC. It just enables access to it.
*
* @note This function is safe to call repeatedly - Tested by ::rtc_init_test
*
* Pseudo Code:
* @code
* void rtc_init()
* {
* // Enable clock gate so processor can read RTC registers
* POWER_CTRL |= POWER_CTRL_RTC_Msk;
*
* // See if the RTC is already setup
* if (!(RTC_STATUS & RTC_STATUS_COUNTING_Msk)) {
*
* // Setup the RTC clock source
* RTC_CTRL |= RTC_CTRL_CLK32_Msk;
* }
* }
* @endcode
*/
void rtc_init(void);
/** Deinitialize RTC
*
* TODO: The function is not used by rtc api in mbed-drivers.
* Powerdown the RTC in preparation for sleep, powerdown or reset.
* After this function is called no other RTC functions should be called
* except for ::rtc_init.
*
* @note This function does not stop the RTC from counting - Tested by ::rtc_persist_test
*
* Pseudo Code:
* @code
* void rtc_free()
* {
* // Disable clock gate since processor no longer needs to read RTC registers
* POWER_CTRL &= ~POWER_CTRL_RTC_Msk;
* }
* @endcode
*/
void rtc_free(void);
/** Get the RTC enable status
/** Check if the RTC has the time set and is counting
*
* @retval 0 disabled
* @retval 1 enabled
* @retval 0 The time reported by the RTC is not valid
* @retval 1 The time has been set the RTC is counting
*
* Pseudo Code:
* @code
* int rtc_isenabled()
* {
* if (RTC_STATUS & RTC_STATUS_COUNTING_Msk) {
* return 1;
* } else {
* return 0;
* }
* }
* @endcode
*/
int rtc_isenabled(void);
/** Get the current time from the RTC peripheral
*
* @return The current time
* @return The current time in seconds
*
* @note Some RTCs are not synchronized with the main clock. If
* this is the case with your RTC then you must read the RTC time
* in a loop to prevent reading the wrong time due to a glitch.
* The test ::rtc_glitch_test is intended to catch this bug.
*
* Example implementation for an unsynchronized ripple counter:
* @code
* time_t rtc_read()
* {
* uint32_t val;
* uint32_t last_val;
*
* // Loop until the same value is read twice
* val = RTC_SECONDS;
* do {
* last_val = val;
* val = RTC_SECONDS;
* } while (last_val != val);
*
* return (time_t)val;
* }
* @endcode
*/
time_t rtc_read(void);
/** Set the current time to the RTC peripheral
/** Write the current time in seconds to the RTC peripheral
*
* @param t The current time to be set
* @param t The current time to be set in seconds.
*
* Pseudo Code:
* @code
* void rtc_write(time_t t)
* {
* RTC_SECONDS = t;
* }
* @endcode
*/
void rtc_write(time_t t);