Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /* mbed Microcontroller Library
00002  * Copyright (c) 2013-2017 ARM Limited
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 #include "mbed.h"
00017 #include "greentea-client/test_env.h"
00018 #include "utest/utest.h"
00019 #include "unity/unity.h"
00020 
00021 
00022 using utest::v1::Case;
00023 
00024 #define ONE_MILLI_SEC 1000
00025 #define TICKER_COUNT 16
00026 #define MULTI_TICKER_TIME_MS 100
00027 volatile uint32_t callback_trigger_count = 0;
00028 static const int test_timeout = 240;
00029 static const int total_ticks = 10;
00030 
00031 
00032 /* Tolerance is quite arbitrary due to large number of boards with varying level of accuracy */
00033 #define TOLERANCE_US 1000
00034 
00035 volatile uint32_t ticker_callback_flag;
00036 volatile uint32_t multi_counter;
00037 
00038 DigitalOut led1(LED1);
00039 DigitalOut led2(LED2);
00040 
00041 Timer gtimer;
00042 volatile int ticker_count = 0;
00043 
00044 
00045 void switch_led1_state(void)
00046 {
00047     // blink 3 times per second
00048     if((callback_trigger_count % 333) == 0) {
00049         led1 = !led1;
00050     }
00051 }
00052 
00053 void switch_led2_state(void)
00054 {
00055     // blink 3 times per second
00056     // make led2 blink at the same callback_trigger_count value as led1
00057     if(((callback_trigger_count - 1) % 333) == 0) {
00058         led2 = !led2;
00059     }
00060 }
00061 
00062 void ticker_callback_1(void)
00063 {
00064     ++callback_trigger_count;
00065     switch_led1_state();
00066 }
00067 
00068 void ticker_callback_2(void)
00069 {
00070     ++callback_trigger_count;
00071     switch_led2_state();
00072 }
00073 
00074 
00075 void sem_release(Semaphore *sem)
00076 {
00077     sem->release();
00078 }
00079 
00080 
00081 void stop_gtimer_set_flag(void)
00082 {
00083     gtimer.stop();
00084     core_util_atomic_incr_u32((uint32_t*)&ticker_callback_flag, 1);
00085 }
00086 
00087 void increment_multi_counter(void)
00088 {
00089     core_util_atomic_incr_u32((uint32_t*)&multi_counter, 1);
00090 }
00091 
00092 
00093 /* Tests is to measure the accuracy of Ticker over a period of time
00094  *
00095  * 1) DUT would start to update callback_trigger_count every milli sec
00096  * 2) Host would query what is current count base_time, Device responds by the callback_trigger_count.
00097  * 3) Host after waiting for measurement stretch. It will query for device time again final_time.
00098  * 4) Host computes the drift considering base_time, final_time, transport delay and measurement stretch
00099  * 5) Finally host send the results back to device pass/fail based on tolerance.
00100  * 6) More details on tests can be found in timing_drift_auto.py
00101  */
00102 void test_case_1x_ticker()
00103 {
00104     char _key[11] = { };
00105     char _value[128] = { };
00106     int expected_key = 1;
00107     Ticker ticker;
00108 
00109     led1 = 1;
00110     led2 = 1;
00111     callback_trigger_count = 0;
00112 
00113     greentea_send_kv("timing_drift_check_start", 0);
00114     ticker.attach_us(&ticker_callback_1, ONE_MILLI_SEC);
00115 
00116     // wait for 1st signal from host
00117     do {
00118         greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
00119         expected_key = strcmp(_key, "base_time");
00120     } while (expected_key);
00121     greentea_send_kv(_key, callback_trigger_count * ONE_MILLI_SEC);
00122 
00123     // wait for 2nd signal from host
00124     greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
00125     greentea_send_kv(_key, callback_trigger_count * ONE_MILLI_SEC);
00126 
00127     //get the results from host
00128     greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
00129 
00130     TEST_ASSERT_EQUAL_STRING_MESSAGE("pass", _key,"Host side script reported a fail...");
00131 }
00132 
00133 /* Tests is to measure the accuracy of Ticker over a period of time
00134  *
00135  * 1) DUT would start to update callback_trigger_count every milli sec, we use 2 tickers
00136  *    to update the count alternatively.
00137  * 2) Host would query what is current count base_time, Device responds by the callback_trigger_count
00138  * 3) Host after waiting for measurement stretch. It will query for device time again final_time.
00139  * 4) Host computes the drift considering base_time, final_time, transport delay and measurement stretch
00140  * 5) Finally host send the results back to device pass/fail based on tolerance.
00141  * 6) More details on tests can be found in timing_drift_auto.py
00142  */
00143 void test_case_2x_ticker()
00144 {
00145     char _key[11] = { };
00146     char _value[128] = { };
00147     int expected_key =  1;
00148     Ticker ticker1, ticker2;
00149 
00150     led1 = 0;
00151     led2 = 1;
00152     callback_trigger_count = 0;
00153 
00154     ticker1.attach_us(ticker_callback_1, 2 * ONE_MILLI_SEC);
00155     // delay second ticker to have a pair of tickers tick every one millisecond
00156     wait_us(ONE_MILLI_SEC);
00157     greentea_send_kv("timing_drift_check_start", 0);
00158     ticker2.attach_us(ticker_callback_2, 2 * ONE_MILLI_SEC);
00159 
00160     // wait for 1st signal from host
00161     do {
00162         greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
00163         expected_key = strcmp(_key, "base_time");
00164     } while (expected_key);
00165     greentea_send_kv(_key, callback_trigger_count * ONE_MILLI_SEC);
00166 
00167     // wait for 2nd signal from host
00168     greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
00169     greentea_send_kv(_key, callback_trigger_count * ONE_MILLI_SEC);
00170 
00171     //get the results from host
00172     greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
00173 
00174     TEST_ASSERT_EQUAL_STRING_MESSAGE("pass", _key,"Host side script reported a fail...");
00175 }
00176 
00177 /** Test many tickers run one after the other
00178 
00179     Given many Tickers
00180     When schedule them one after the other with the same time intervals
00181     Then tickers properly execute callbacks
00182     When schedule them one after the other with the different time intervals
00183     Then tickers properly execute callbacks
00184  */
00185 void test_multi_ticker(void)
00186 {
00187     Ticker ticker[TICKER_COUNT];
00188     const uint32_t extra_wait = 5; // extra 5ms wait time
00189 
00190     multi_counter = 0;
00191     for (int i = 0; i < TICKER_COUNT; i++) {
00192         ticker[i].attach_us(callback(increment_multi_counter), MULTI_TICKER_TIME_MS * 1000);
00193     }
00194 
00195     Thread::wait(MULTI_TICKER_TIME_MS + extra_wait);
00196     for (int i = 0; i < TICKER_COUNT; i++) {
00197             ticker[i].detach();
00198     }
00199     TEST_ASSERT_EQUAL(TICKER_COUNT, multi_counter);
00200 
00201     multi_counter = 0;
00202     for (int i = 0; i < TICKER_COUNT; i++) {
00203         ticker[i].attach_us(callback(increment_multi_counter), (MULTI_TICKER_TIME_MS + i) * 1000);
00204     }
00205 
00206     Thread::wait(MULTI_TICKER_TIME_MS + TICKER_COUNT + extra_wait);
00207     for (int i = 0; i < TICKER_COUNT; i++) {
00208         ticker[i].detach();
00209     }
00210     TEST_ASSERT_EQUAL(TICKER_COUNT, multi_counter);
00211 }
00212 
00213 /** Test multi callback time
00214 
00215     Given a Ticker
00216     When the callback is attached multiple times
00217     Then ticker properly execute callback multiple times
00218  */
00219 void test_multi_call_time(void)
00220 {
00221     Ticker ticker;
00222     int time_diff;
00223     const int attach_count = 10;
00224 
00225     for (int i = 0; i < attach_count; i++) {
00226         ticker_callback_flag = 0;
00227         gtimer.reset();
00228 
00229         gtimer.start();
00230         ticker.attach_us(callback(stop_gtimer_set_flag), MULTI_TICKER_TIME_MS * 1000);
00231         while(!ticker_callback_flag);
00232         time_diff = gtimer.read_us();
00233 
00234         TEST_ASSERT_UINT32_WITHIN(TOLERANCE_US, MULTI_TICKER_TIME_MS * 1000, time_diff);
00235     }
00236 }
00237 
00238 /** Test if detach cancel scheduled callback event
00239 
00240     Given a Ticker with callback attached
00241     When the callback is detached
00242     Then the callback is not being called
00243  */
00244 void test_detach(void)
00245 {
00246     Ticker ticker;
00247     int32_t ret;
00248     const float ticker_time_s = 0.1f;
00249     const uint32_t wait_time_ms = 500;
00250     Semaphore sem(0, 1);
00251 
00252     ticker.attach(callback(sem_release, &sem), ticker_time_s);
00253 
00254     ret = sem.wait();
00255     TEST_ASSERT_TRUE(ret > 0);
00256 
00257     ret = sem.wait();
00258     ticker.detach(); /* cancel */
00259     TEST_ASSERT_TRUE(ret > 0);
00260 
00261     ret = sem.wait(wait_time_ms);
00262     TEST_ASSERT_EQUAL(0, ret);
00263 }
00264 
00265 /** Test single callback time via attach
00266 
00267     Given a Ticker
00268     When callback attached with time interval specified
00269     Then ticker properly executes callback within a specified time interval
00270  */
00271 template<us_timestamp_t DELAY_US>
00272 void test_attach_time(void)
00273 {
00274     Ticker ticker;
00275     ticker_callback_flag = 0;
00276 
00277     gtimer.reset();
00278     gtimer.start();
00279     ticker.attach(callback(stop_gtimer_set_flag), ((float)DELAY_US) / 1000000.0f);
00280     while(!ticker_callback_flag);
00281     ticker.detach();
00282     const int time_diff = gtimer.read_us();
00283 
00284     TEST_ASSERT_UINT64_WITHIN(TOLERANCE_US, DELAY_US, time_diff);
00285 }
00286 
00287 /** Test single callback time via attach_us
00288 
00289     Given a Ticker
00290     When callback attached with time interval specified
00291     Then ticker properly executes callback within a specified time interval
00292  */
00293 template<us_timestamp_t DELAY_US>
00294 void test_attach_us_time(void)
00295 {
00296     Ticker ticker;
00297     ticker_callback_flag = 0;
00298 
00299     gtimer.reset();
00300     gtimer.start();
00301     ticker.attach_us(callback(stop_gtimer_set_flag), DELAY_US);
00302     while(!ticker_callback_flag);
00303     ticker.detach();
00304     const int time_diff = gtimer.read_us();
00305 
00306     TEST_ASSERT_UINT64_WITHIN(TOLERANCE_US, DELAY_US, time_diff);
00307 }
00308 
00309 
00310 // Test cases
00311 Case cases[] = {
00312     Case("Test attach for 0.01s and time measure", test_attach_time<10000>),
00313     Case("Test attach_us for 10ms and time measure", test_attach_us_time<10000>),
00314     Case("Test attach for 0.1s and time measure", test_attach_time<100000>),
00315     Case("Test attach_us for 100ms and time measure", test_attach_us_time<100000>),
00316     Case("Test attach for 0.5s and time measure", test_attach_time<500000>),
00317     Case("Test attach_us for 500ms and time measure", test_attach_us_time<500000>),
00318     Case("Test detach", test_detach),
00319     Case("Test multi call and time measure", test_multi_call_time),
00320     Case("Test multi ticker", test_multi_ticker),
00321     Case("Test timers: 1x ticker", test_case_1x_ticker),
00322     Case("Test timers: 2x ticker", test_case_2x_ticker)
00323 };
00324 
00325 utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
00326 {
00327     GREENTEA_SETUP(test_timeout, "timing_drift_auto");
00328     return utest::v1::greentea_test_setup_handler(number_of_cases);
00329 }
00330 
00331 utest::v1::Specification specification(greentea_test_setup, cases, utest::v1::greentea_test_teardown_handler);
00332 
00333 int main()
00334 {
00335     utest::v1::Harness::run(specification);
00336 }