4516 lines
170 KiB
XML
4516 lines
170 KiB
XML
|
<?xml version="1.0" encoding="UTF-8"?>
|
||
|
<SPC5-Config version="1.0.0">
|
||
|
<application name="ChibiOS/RT Test Suite" version="1.0.0" standalone="true" locked="false">
|
||
|
<description>Test Specification for ChibiOS/RT.</description>
|
||
|
<component id="org.chibios.spc5.components.portable.generic_startup">
|
||
|
<component id="org.chibios.spc5.components.portable.chibios_unitary_tests_engine" />
|
||
|
</component>
|
||
|
<instances>
|
||
|
<instance locked="false" id="org.chibios.spc5.components.portable.generic_startup" />
|
||
|
<instance locked="false" id="org.chibios.spc5.components.portable.chibios_unitary_tests_engine">
|
||
|
<description>
|
||
|
<brief>
|
||
|
<value>ChibiOS/RT Test Suite.</value>
|
||
|
</brief>
|
||
|
<copyright>
|
||
|
<value><![CDATA[/*
|
||
|
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
|
||
|
|
||
|
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.
|
||
|
*/]]></value>
|
||
|
</copyright>
|
||
|
<introduction>
|
||
|
<value>Test suite for ChibiOS/RT. The purpose of this suite is to perform unit tests on the RT modules and to converge to 100% code coverage through successive improvements.</value>
|
||
|
</introduction>
|
||
|
</description>
|
||
|
<global_data_and_code>
|
||
|
<code_prefix>
|
||
|
<value>rt_</value>
|
||
|
</code_prefix>
|
||
|
<global_definitions>
|
||
|
<value><![CDATA[/*
|
||
|
* Allowed delay in timeout checks.
|
||
|
*/
|
||
|
#define ALLOWED_DELAY TIME_MS2I(2)
|
||
|
|
||
|
/*
|
||
|
* Maximum number of test threads.
|
||
|
*/
|
||
|
#define MAX_THREADS 5
|
||
|
|
||
|
/*
|
||
|
* Stack size of test threads.
|
||
|
*/
|
||
|
#if defined(PORT_ARCHITECTURE_AVR) || defined(PORT__ARCHITECTURE_MSP430)
|
||
|
#define THREADS_STACK_SIZE 48
|
||
|
#elif defined(PORT__ARCHITECTURE_STM8)
|
||
|
#define THREADS_STACK_SIZE 64
|
||
|
#elif defined(PORT__ARCHITECTURE_SIMIA32)
|
||
|
#define THREADS_STACK_SIZE 512
|
||
|
#else
|
||
|
#define THREADS_STACK_SIZE 128
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* Working Area size of test threads.
|
||
|
*/
|
||
|
#define WA_SIZE MEM_ALIGN_NEXT(THD_WORKING_AREA_SIZE(THREADS_STACK_SIZE), \
|
||
|
PORT_WORKING_AREA_ALIGN)
|
||
|
|
||
|
extern uint8_t test_buffer[WA_SIZE * 5];
|
||
|
extern thread_t *threads[MAX_THREADS];
|
||
|
extern void * ROMCONST wa[5];
|
||
|
|
||
|
void test_print_port_info(void);
|
||
|
void test_terminate_threads(void);
|
||
|
void test_wait_threads(void);
|
||
|
systime_t test_wait_tick(void);]]></value>
|
||
|
</global_definitions>
|
||
|
<global_code>
|
||
|
<value><![CDATA[/*
|
||
|
* Global test buffer holding 5 working areas.
|
||
|
*/
|
||
|
ALIGNED_VAR(PORT_WORKING_AREA_ALIGN) uint8_t test_buffer[WA_SIZE * 5];
|
||
|
|
||
|
/*
|
||
|
* Pointers to the spawned threads.
|
||
|
*/
|
||
|
thread_t *threads[MAX_THREADS];
|
||
|
|
||
|
/*
|
||
|
* Pointers to the working areas.
|
||
|
*/
|
||
|
void * ROMCONST wa[5] = {test_buffer + (WA_SIZE * 0),
|
||
|
test_buffer + (WA_SIZE * 1),
|
||
|
test_buffer + (WA_SIZE * 2),
|
||
|
test_buffer + (WA_SIZE * 3),
|
||
|
test_buffer + (WA_SIZE * 4)};
|
||
|
|
||
|
/*
|
||
|
* Sets a termination request in all the test-spawned threads.
|
||
|
*/
|
||
|
void test_terminate_threads(void) {
|
||
|
unsigned i;
|
||
|
|
||
|
for (i = 0; i < MAX_THREADS; i++)
|
||
|
if (threads[i])
|
||
|
chThdTerminate(threads[i]);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Waits for the completion of all the test-spawned threads.
|
||
|
*/
|
||
|
void test_wait_threads(void) {
|
||
|
unsigned i;
|
||
|
|
||
|
for (i = 0; i < MAX_THREADS; i++)
|
||
|
if (threads[i] != NULL) {
|
||
|
chThdWait(threads[i]);
|
||
|
threads[i] = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Delays execution until next system time tick.
|
||
|
*/
|
||
|
systime_t test_wait_tick(void) {
|
||
|
|
||
|
chThdSleep(1);
|
||
|
return chVTGetSystemTime();
|
||
|
}]]></value>
|
||
|
</global_code>
|
||
|
</global_data_and_code>
|
||
|
<sequences>
|
||
|
<sequence>
|
||
|
<type index="0">
|
||
|
<value>Internal Tests</value>
|
||
|
</type>
|
||
|
<brief>
|
||
|
<value>Information.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>This sequence reports configuration and version information about the RT kernel.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<shared_code>
|
||
|
<value><![CDATA[#include "ch.h"]]></value>
|
||
|
</shared_code>
|
||
|
<cases>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Port Info.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Port-related info are reported.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value />
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Prints the version string.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[#if defined(PORT_ARCHITECTURE_NAME)
|
||
|
test_print("--- Architecture: ");
|
||
|
test_println(PORT_ARCHITECTURE_NAME);
|
||
|
#endif
|
||
|
#if defined(PORT_CORE_VARIANT_NAME)
|
||
|
test_print("--- Core Variant: ");
|
||
|
test_println(PORT_CORE_VARIANT_NAME);
|
||
|
#endif
|
||
|
#if defined(PORT_COMPILER_NAME)
|
||
|
test_print("--- Compiler: ");
|
||
|
test_println(PORT_COMPILER_NAME);
|
||
|
#endif
|
||
|
#if defined(PORT_INFO)
|
||
|
test_print("--- Port Info: ");
|
||
|
test_println(PORT_INFO);
|
||
|
#endif
|
||
|
#if defined(PORT_NATURAL_ALIGN)
|
||
|
test_print("--- Natural alignment: ");
|
||
|
test_printn(PORT_NATURAL_ALIGN);
|
||
|
test_println("");
|
||
|
#endif
|
||
|
#if defined(PORT_STACK_ALIGN)
|
||
|
test_print("--- Stack alignment: ");
|
||
|
test_printn(PORT_STACK_ALIGN);
|
||
|
test_println("");
|
||
|
#endif
|
||
|
#if defined(PORT_WORKING_AREA_ALIGN)
|
||
|
test_print("--- Working area alignment: ");
|
||
|
test_printn(PORT_WORKING_AREA_ALIGN);
|
||
|
test_println("");
|
||
|
#endif]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Kernel Info.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>The version numbers are reported.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value />
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Prints the version string.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[
|
||
|
test_println("--- Product: ChibiOS/RT");
|
||
|
test_print("--- Stable Flag: ");
|
||
|
test_printn(CH_KERNEL_STABLE);
|
||
|
test_println("");
|
||
|
test_print("--- Version String: ");
|
||
|
test_println(CH_KERNEL_VERSION);
|
||
|
test_print("--- Major Number: ");
|
||
|
test_printn(CH_KERNEL_MAJOR);
|
||
|
test_println("");
|
||
|
test_print("--- Minor Number: ");
|
||
|
test_printn(CH_KERNEL_MINOR);
|
||
|
test_println("");
|
||
|
test_print("--- Patch Number: ");
|
||
|
test_printn(CH_KERNEL_PATCH);
|
||
|
test_println("");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Kernel Settings.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>The static kernel settings are reported.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value />
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Prints the configuration options settings.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_print("--- CH_CFG_ST_RESOLUTION: ");
|
||
|
test_printn(CH_CFG_ST_RESOLUTION);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_ST_FREQUENCY: ");
|
||
|
test_printn(CH_CFG_ST_FREQUENCY);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_INTERVALS_SIZE: ");
|
||
|
test_printn(CH_CFG_INTERVALS_SIZE);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_TIME_TYPES_SIZE: ");
|
||
|
test_printn(CH_CFG_TIME_TYPES_SIZE);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_ST_TIMEDELTA: ");
|
||
|
test_printn(CH_CFG_ST_TIMEDELTA);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_TIME_QUANTUM: ");
|
||
|
test_printn(CH_CFG_TIME_QUANTUM);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_MEMCORE_SIZE: ");
|
||
|
test_printn(CH_CFG_MEMCORE_SIZE);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_NO_IDLE_THREAD: ");
|
||
|
test_printn(CH_CFG_NO_IDLE_THREAD);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_OPTIMIZE_SPEED: ");
|
||
|
test_printn(CH_CFG_OPTIMIZE_SPEED);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_TM: ");
|
||
|
test_printn(CH_CFG_USE_TM);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_REGISTRY: ");
|
||
|
test_printn(CH_CFG_USE_REGISTRY);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_WAITEXIT: ");
|
||
|
test_printn(CH_CFG_USE_WAITEXIT);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_SEMAPHORES: ");
|
||
|
test_printn(CH_CFG_USE_SEMAPHORES);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_SEMAPHORES_PRIORITY: ");
|
||
|
test_printn(CH_CFG_USE_SEMAPHORES_PRIORITY);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_MUTEXES: ");
|
||
|
test_printn(CH_CFG_USE_MUTEXES);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_MUTEXES_RECURSIVE: ");
|
||
|
test_printn(CH_CFG_USE_MUTEXES_RECURSIVE);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_CONDVARS: ");
|
||
|
test_printn(CH_CFG_USE_CONDVARS);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_CONDVARS_TIMEOUT: ");
|
||
|
test_printn(CH_CFG_USE_CONDVARS_TIMEOUT);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_EVENTS: ");
|
||
|
test_printn(CH_CFG_USE_EVENTS);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_EVENTS_TIMEOUT: ");
|
||
|
test_printn(CH_CFG_USE_EVENTS_TIMEOUT);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_MESSAGES: ");
|
||
|
test_printn(CH_CFG_USE_MESSAGES);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_MESSAGES_PRIORITY: ");
|
||
|
test_printn(CH_CFG_USE_MESSAGES_PRIORITY);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_MAILBOXES: ");
|
||
|
test_printn(CH_CFG_USE_MAILBOXES);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_MEMCORE: ");
|
||
|
test_printn(CH_CFG_USE_MEMCORE);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_HEAP: ");
|
||
|
test_printn(CH_CFG_USE_HEAP);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_MEMPOOLS: ");
|
||
|
test_printn(CH_CFG_USE_MEMPOOLS);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_OBJ_FIFOS: ");
|
||
|
test_printn(CH_CFG_USE_OBJ_FIFOS);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_DYNAMIC: ");
|
||
|
test_printn(CH_CFG_USE_DYNAMIC);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_USE_FACTORY: ");
|
||
|
test_printn(CH_CFG_USE_FACTORY);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_FACTORY_MAX_NAMES_LENGTH: ");
|
||
|
test_printn(CH_CFG_FACTORY_MAX_NAMES_LENGTH);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_FACTORY_OBJECTS_REGISTRY: ");
|
||
|
test_printn(CH_CFG_FACTORY_OBJECTS_REGISTRY);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_FACTORY_GENERIC_BUFFERS: ");
|
||
|
test_printn(CH_CFG_FACTORY_GENERIC_BUFFERS);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_FACTORY_SEMAPHORES: ");
|
||
|
test_printn(CH_CFG_FACTORY_SEMAPHORES);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_FACTORY_MAILBOXES: ");
|
||
|
test_printn(CH_CFG_FACTORY_MAILBOXES);
|
||
|
test_println("");
|
||
|
test_print("--- CH_CFG_FACTORY_OBJ_FIFOS: ");
|
||
|
test_printn(CH_CFG_FACTORY_OBJ_FIFOS);
|
||
|
test_println("");
|
||
|
test_print("--- CH_DBG_STATISTICS: ");
|
||
|
test_printn(CH_DBG_STATISTICS);
|
||
|
test_println("");
|
||
|
test_print("--- CH_DBG_SYSTEM_STATE_CHECK: ");
|
||
|
test_printn(CH_DBG_SYSTEM_STATE_CHECK);
|
||
|
test_println("");
|
||
|
test_print("--- CH_DBG_ENABLE_CHECKS: ");
|
||
|
test_printn(CH_DBG_ENABLE_CHECKS);
|
||
|
test_println("");
|
||
|
test_print("--- CH_DBG_ENABLE_ASSERTS: ");
|
||
|
test_printn(CH_DBG_ENABLE_ASSERTS);
|
||
|
test_println("");
|
||
|
test_print("--- CH_DBG_TRACE_MASK: ");
|
||
|
test_printn(CH_DBG_TRACE_MASK);
|
||
|
test_println("");
|
||
|
test_print("--- CH_DBG_TRACE_BUFFER_SIZE: ");
|
||
|
test_printn(CH_DBG_TRACE_BUFFER_SIZE);
|
||
|
test_println("");
|
||
|
test_print("--- CH_DBG_ENABLE_STACK_CHECK: ");
|
||
|
test_printn(CH_DBG_ENABLE_STACK_CHECK);
|
||
|
test_println("");
|
||
|
test_print("--- CH_DBG_FILL_THREADS: ");
|
||
|
test_printn(CH_DBG_FILL_THREADS);
|
||
|
test_println("");
|
||
|
test_print("--- CH_DBG_THREADS_PROFILING: ");
|
||
|
test_printn(CH_DBG_THREADS_PROFILING);
|
||
|
test_println("");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
</cases>
|
||
|
</sequence>
|
||
|
<sequence>
|
||
|
<type index="0">
|
||
|
<value>Internal Tests</value>
|
||
|
</type>
|
||
|
<brief>
|
||
|
<value>System layer and port interface.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>The functionality of the system layer and port interface is tested. Basic RT functionality is taken for granted or this test suite could not even be executed. Errors in implementation are detected by executing this sequence with the state checker enabled (CH_DBG_STATE_CHECKER=TRUE).</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<shared_code>
|
||
|
<value><![CDATA[/* Timer callback for testing system functions in ISR context.*/
|
||
|
static void vtcb(void *p) {
|
||
|
syssts_t sts;
|
||
|
|
||
|
(void)p;
|
||
|
|
||
|
/* Testing normal case.*/
|
||
|
chSysLockFromISR();
|
||
|
chSysUnlockFromISR();
|
||
|
|
||
|
/* Reentrant case.*/
|
||
|
chSysLockFromISR();
|
||
|
sts = chSysGetStatusAndLockX();
|
||
|
chSysRestoreStatusX(sts);
|
||
|
chSysUnlockFromISR();
|
||
|
}]]></value>
|
||
|
</shared_code>
|
||
|
<cases>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>System integrity functionality.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>The system self-test functionality is invoked in order to make an initial system state assessment and for coverage.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[bool result;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing Ready List integrity.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chSysLock();
|
||
|
result = chSysIntegrityCheckI(CH_INTEGRITY_RLIST);
|
||
|
chSysUnlock();
|
||
|
test_assert(result == false, "ready list check failed");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing Virtual Timers List integrity.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chSysLock();
|
||
|
result = chSysIntegrityCheckI(CH_INTEGRITY_VTLIST);
|
||
|
chSysUnlock();
|
||
|
test_assert(result == false, "virtual timers list check failed");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing Registry List integrity.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chSysLock();
|
||
|
result = chSysIntegrityCheckI(CH_INTEGRITY_REGISTRY);
|
||
|
chSysUnlock();
|
||
|
test_assert(result == false, "registry list check failed");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing Port-defined integrity.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chSysLock();
|
||
|
result = chSysIntegrityCheckI(CH_INTEGRITY_PORT);
|
||
|
chSysUnlock();
|
||
|
test_assert(result == false, "port layer check failed");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Critical zones functionality.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>The critical zones API is invoked for coverage.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[syssts_t sts;
|
||
|
virtual_timer_t vt;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing chSysGetStatusAndLockX() and chSysRestoreStatusX(), non reentrant case.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[sts = chSysGetStatusAndLockX();
|
||
|
chSysRestoreStatusX(sts);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing chSysGetStatusAndLockX() and chSysRestoreStatusX(), reentrant case.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chSysLock();
|
||
|
sts = chSysGetStatusAndLockX();
|
||
|
chSysRestoreStatusX(sts);
|
||
|
chSysUnlock();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing chSysUnconditionalLock().</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chSysUnconditionalLock();
|
||
|
chSysUnconditionalLock();
|
||
|
chSysUnlock();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing chSysUnconditionalUnlock().</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chSysLock();
|
||
|
chSysUnconditionalUnlock();
|
||
|
chSysUnconditionalUnlock();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing from ISR context using a virtual timer.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chVTObjectInit(&vt);
|
||
|
chVTSet(&vt, 1, vtcb, NULL);
|
||
|
chThdSleep(10);
|
||
|
|
||
|
test_assert(chVTIsArmed(&vt) == false, "timer still armed");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Interrupts handling functionality.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>The interrupts handling API is invoked for coverage.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value />
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing chSysSuspend(), chSysDisable() and chSysEnable().</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chSysSuspend();
|
||
|
chSysDisable();
|
||
|
chSysSuspend();
|
||
|
chSysEnable();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>System Tick Counter functionality.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>The functionality of the API @p chVTGetSystemTimeX() is tested.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value />
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>A System Tick Counter increment is expected, the test simply hangs if it does not happen.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[systime_t time = chVTGetSystemTimeX();
|
||
|
while (time == chVTGetSystemTimeX()) {
|
||
|
#if defined(SIMULATOR)
|
||
|
_sim_check_for_interrupts();
|
||
|
#endif
|
||
|
}]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
</cases>
|
||
|
</sequence>
|
||
|
<sequence>
|
||
|
<type index="0">
|
||
|
<value>Internal Tests</value>
|
||
|
</type>
|
||
|
<brief>
|
||
|
<value>Threads Functionality.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>This sequence tests the ChibiOS/RT functionalities related to threading.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<shared_code>
|
||
|
<value><![CDATA[static THD_FUNCTION(thread, p) {
|
||
|
|
||
|
test_emit_token(*(char *)p);
|
||
|
}]]></value>
|
||
|
</shared_code>
|
||
|
<cases>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Thread Sleep functionality.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>The functionality of @p chThdSleep() and derivatives is tested.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[systime_t time;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The current system time is read then a sleep is performed for 100 system ticks and on exit the system time is verified again.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[time = chVTGetSystemTimeX();
|
||
|
chThdSleep(100);
|
||
|
test_assert_time_window(chTimeAddX(time, 100),
|
||
|
chTimeAddX(time, 100 + CH_CFG_ST_TIMEDELTA + 1),
|
||
|
"out of time window");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The current system time is read then a sleep is performed for 100000 microseconds and on exit the system time is verified again.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[time = chVTGetSystemTimeX();
|
||
|
chThdSleepMicroseconds(100000);
|
||
|
test_assert_time_window(chTimeAddX(time, TIME_US2I(100000)),
|
||
|
chTimeAddX(time, TIME_US2I(100000) + CH_CFG_ST_TIMEDELTA + 1),
|
||
|
"out of time window");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The current system time is read then a sleep is performed for 100 milliseconds and on exit the system time is verified again.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[time = chVTGetSystemTimeX();
|
||
|
chThdSleepMilliseconds(100);
|
||
|
test_assert_time_window(chTimeAddX(time, TIME_MS2I(100)),
|
||
|
chTimeAddX(time, TIME_MS2I(100) + CH_CFG_ST_TIMEDELTA + 1),
|
||
|
"out of time window");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The current system time is read then a sleep is performed for 1 second and on exit the system time is verified again.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[time = chVTGetSystemTimeX();
|
||
|
chThdSleepSeconds(1);
|
||
|
test_assert_time_window(chTimeAddX(time, TIME_S2I(1)),
|
||
|
chTimeAddX(time, TIME_S2I(1) + CH_CFG_ST_TIMEDELTA + 1),
|
||
|
"out of time window");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Function chThdSleepUntil() is tested with a timeline of "now" + 100 ticks.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[time = chVTGetSystemTimeX();
|
||
|
chThdSleepUntil(chTimeAddX(time, 100));
|
||
|
test_assert_time_window(chTimeAddX(time, 100),
|
||
|
chTimeAddX(time, 100 + CH_CFG_ST_TIMEDELTA + 1),
|
||
|
"out of time window");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Ready List functionality, threads priority order.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Five threads, are enqueued in the ready list and atomically executed. The test expects the threads to perform their operations in correct priority order regardless of the initial order.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value />
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Creating 5 threads with increasing priority, execution sequence is tested.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-5, thread, "E");
|
||
|
threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-4, thread, "D");
|
||
|
threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-3, thread, "C");
|
||
|
threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()-2, thread, "B");
|
||
|
threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()-1, thread, "A");
|
||
|
test_wait_threads();
|
||
|
test_assert_sequence("ABCDE", "invalid sequence");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Creating 5 threads with decreasing priority, execution sequence is tested.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()-1, thread, "A");
|
||
|
threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()-2, thread, "B");
|
||
|
threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-3, thread, "C");
|
||
|
threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-4, thread, "D");
|
||
|
threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-5, thread, "E");
|
||
|
test_wait_threads();
|
||
|
test_assert_sequence("ABCDE", "invalid sequence");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Creating 5 threads with pseudo-random priority, execution sequence is tested.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-4, thread, "D");
|
||
|
threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-5, thread, "E");
|
||
|
threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()-1, thread, "A");
|
||
|
threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()-2, thread, "B");
|
||
|
threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-3, thread, "C");
|
||
|
test_wait_threads();
|
||
|
test_assert_sequence("ABCDE", "invalid sequence");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Priority change test.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>A series of priority changes are performed on the current thread in order to verify that the priority change happens as expected.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[tprio_t prio, p1;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Thread priority is increased by one then a check is performed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[prio = chThdGetPriorityX();
|
||
|
p1 = chThdSetPriority(prio + 1);
|
||
|
test_assert(p1 == prio, "unexpected returned priority level");
|
||
|
test_assert(chThdGetPriorityX() == prio + 1, "unexpected priority level");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Thread priority is returned to the previous value then a check is performed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[p1 = chThdSetPriority(p1);
|
||
|
test_assert(p1 == prio + 1, "unexpected returned priority level");
|
||
|
test_assert(chThdGetPriorityX() == prio, "unexpected priority level");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Priority change test with Priority Inheritance.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>A series of priority changes are performed on the current thread in order to verify that the priority change happens as expected.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_MUTEXES</value>
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[tprio_t prio, p1;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Simulating a priority boost situation (prio > realprio).</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[prio = chThdGetPriorityX();
|
||
|
chThdGetSelfX()->prio += 2;
|
||
|
test_assert(chThdGetPriorityX() == prio + 2, "unexpected priority level");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Raising thread priority above original priority but below the boosted level.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[p1 = chThdSetPriority(prio + 1);
|
||
|
test_assert(p1 == prio, "unexpected returned priority level");
|
||
|
test_assert(chThdGetSelfX()->prio == prio + 2, "unexpected priority level");
|
||
|
test_assert(chThdGetSelfX()->realprio == prio + 1, "unexpected returned real priority level");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Raising thread priority above the boosted level.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[p1 = chThdSetPriority(prio + 3);
|
||
|
test_assert(p1 == prio + 1, "unexpected returned priority level");
|
||
|
test_assert(chThdGetSelfX()->prio == prio + 3, "unexpected priority level");
|
||
|
test_assert(chThdGetSelfX()->realprio == prio + 3, "unexpected real priority level");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Restoring original conditions.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chSysLock();
|
||
|
chThdGetSelfX()->prio = prio;
|
||
|
chThdGetSelfX()->realprio = prio;
|
||
|
chSysUnlock();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
</cases>
|
||
|
</sequence>
|
||
|
<sequence>
|
||
|
<type index="0">
|
||
|
<value>Internal Tests</value>
|
||
|
</type>
|
||
|
<brief>
|
||
|
<value>Suspend/Resume.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>This sequence tests the ChibiOS/RT functionalities related to threads suspend/resume.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<shared_code>
|
||
|
<value><![CDATA[static thread_reference_t tr1;
|
||
|
|
||
|
static THD_FUNCTION(thread1, p) {
|
||
|
|
||
|
chSysLock();
|
||
|
chThdResumeI(&tr1, MSG_OK);
|
||
|
chSchRescheduleS();
|
||
|
chSysUnlock();
|
||
|
test_emit_token(*(char *)p);
|
||
|
}]]></value>
|
||
|
</shared_code>
|
||
|
<cases>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Suspend and Resume functionality.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>The functionality of chThdSuspendTimeoutS() and chThdResumeI() is tested.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[tr1 = NULL;]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[systime_t time;
|
||
|
msg_t msg;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The function chThdSuspendTimeoutS() is invoked, the thread is remotely resumed with message @p MSG_OK. On return the message and the state of the reference are tested.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-1, thread1, "A");
|
||
|
chSysLock();
|
||
|
msg = chThdSuspendTimeoutS(&tr1, TIME_INFINITE);
|
||
|
chSysUnlock();
|
||
|
test_assert(NULL == tr1, "not NULL");
|
||
|
test_assert(MSG_OK == msg,"wrong returned message");
|
||
|
test_wait_threads();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The function chThdSuspendTimeoutS() is invoked, the thread is not resumed so a timeout must occur. On return the message and the state of the reference are tested.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chSysLock();
|
||
|
time = chVTGetSystemTimeX();
|
||
|
msg = chThdSuspendTimeoutS(&tr1, TIME_MS2I(1000));
|
||
|
chSysUnlock();
|
||
|
test_assert_time_window(chTimeAddX(time, TIME_MS2I(1000)),
|
||
|
chTimeAddX(time, TIME_MS2I(1000) + CH_CFG_ST_TIMEDELTA + 1),
|
||
|
"out of time window");
|
||
|
test_assert(NULL == tr1, "not NULL");
|
||
|
test_assert(MSG_TIMEOUT == msg, "wrong returned message");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
</cases>
|
||
|
</sequence>
|
||
|
<sequence>
|
||
|
<type index="0">
|
||
|
<value>Internal Tests</value>
|
||
|
</type>
|
||
|
<brief>
|
||
|
<value>Counter Semaphores.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>This sequence tests the ChibiOS/RT functionalities related to counter semaphores.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_SEMAPHORES</value>
|
||
|
</condition>
|
||
|
<shared_code>
|
||
|
<value><![CDATA[#include "ch.h"
|
||
|
|
||
|
static semaphore_t sem1;
|
||
|
|
||
|
static THD_FUNCTION(thread1, p) {
|
||
|
|
||
|
chSemWait(&sem1);
|
||
|
test_emit_token(*(char *)p);
|
||
|
}
|
||
|
|
||
|
static THD_FUNCTION(thread2, p) {
|
||
|
|
||
|
(void)p;
|
||
|
chThdSleepMilliseconds(50);
|
||
|
chSysLock();
|
||
|
chSemSignalI(&sem1); /* For coverage reasons */
|
||
|
chSchRescheduleS();
|
||
|
chSysUnlock();
|
||
|
}
|
||
|
|
||
|
static THD_FUNCTION(thread3, p) {
|
||
|
|
||
|
(void)p;
|
||
|
chSemWait(&sem1);
|
||
|
chSemSignal(&sem1);
|
||
|
}
|
||
|
|
||
|
static THD_FUNCTION(thread4, p) {
|
||
|
|
||
|
chBSemSignal((binary_semaphore_t *)p);
|
||
|
}]]></value>
|
||
|
</shared_code>
|
||
|
<cases>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Semaphore primitives, no state change.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Wait, Signal and Reset primitives are tested. The testing thread does not trigger a state change.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chSemObjectInit(&sem1, 1);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value><![CDATA[chSemReset(&sem1, 0);]]></value>
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value />
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The function chSemWait() is invoked, after return the counter and the returned message are tested.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[msg_t msg;
|
||
|
|
||
|
msg = chSemWait(&sem1);
|
||
|
test_assert_lock(chSemGetCounterI(&sem1) == 0, "wrong counter value");
|
||
|
test_assert(MSG_OK == msg, "wrong returned message");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The function chSemSignal() is invoked, after return the counter is tested.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chSemSignal(&sem1);
|
||
|
test_assert_lock(chSemGetCounterI(&sem1) == 1, "wrong counter value");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The function chSemReset() is invoked, after return the counter is tested.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chSemReset(&sem1, 2);
|
||
|
test_assert_lock(chSemGetCounterI(&sem1) == 2, "wrong counter value");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Semaphore enqueuing test.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Five threads with randomized priorities are enqueued to a semaphore then awakened one at time. The test expects that the threads reach their goal in FIFO order or priority order depending on the @p CH_CFG_USE_SEMAPHORES_PRIORITY configuration setting.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chSemObjectInit(&sem1, 0);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value />
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Five threads are created with mixed priority levels (not increasing nor decreasing). Threads enqueue on a semaphore initialized to zero.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+5, thread1, "A");
|
||
|
threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()+1, thread1, "B");
|
||
|
threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()+3, thread1, "C");
|
||
|
threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()+4, thread1, "D");
|
||
|
threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()+2, thread1, "E");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The semaphore is signaled 5 times. The thread activation sequence is tested.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chSemSignal(&sem1);
|
||
|
chSemSignal(&sem1);
|
||
|
chSemSignal(&sem1);
|
||
|
chSemSignal(&sem1);
|
||
|
chSemSignal(&sem1);
|
||
|
test_wait_threads();
|
||
|
#if CH_CFG_USE_SEMAPHORES_PRIORITY
|
||
|
test_assert_sequence("ADCEB", "invalid sequence");
|
||
|
#else
|
||
|
test_assert_sequence("ABCDE", "invalid sequence");
|
||
|
#endif]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Semaphore timeout test.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>The three possible semaphore waiting modes (do not wait, wait with timeout, wait without timeout) are explored. The test expects that the semaphore wait function returns the correct value in each of the above scenario and that the semaphore structure status is correct after each operation.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chSemObjectInit(&sem1, 0);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[unsigned i;
|
||
|
systime_t target_time;
|
||
|
msg_t msg;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing special case TIME_IMMEDIATE.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[msg = chSemWaitTimeout(&sem1, TIME_IMMEDIATE);
|
||
|
test_assert(msg == MSG_TIMEOUT, "wrong wake-up message");
|
||
|
test_assert(queue_isempty(&sem1.queue), "queue not empty");
|
||
|
test_assert(sem1.cnt == 0, "counter not zero");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing non-timeout condition.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX() - 1,
|
||
|
thread2, 0);
|
||
|
msg = chSemWaitTimeout(&sem1, TIME_MS2I(500));
|
||
|
test_wait_threads();
|
||
|
test_assert(msg == MSG_OK, "wrong wake-up message");
|
||
|
test_assert(queue_isempty(&sem1.queue), "queue not empty");
|
||
|
test_assert(sem1.cnt == 0, "counter not zero");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing timeout condition.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[target_time = chTimeAddX(test_wait_tick(), TIME_MS2I(5 * 50));
|
||
|
for (i = 0; i < 5; i++) {
|
||
|
test_emit_token('A' + i);
|
||
|
msg = chSemWaitTimeout(&sem1, TIME_MS2I(50));
|
||
|
test_assert(msg == MSG_TIMEOUT, "wrong wake-up message");
|
||
|
test_assert(queue_isempty(&sem1.queue), "queue not empty");
|
||
|
test_assert(sem1.cnt == 0, "counter not zero");
|
||
|
}
|
||
|
test_assert_sequence("ABCDE", "invalid sequence");
|
||
|
test_assert_time_window(target_time,
|
||
|
chTimeAddX(target_time, ALLOWED_DELAY),
|
||
|
"out of time window");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Testing chSemAddCounterI() functionality.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>The functon is tested by waking up a thread then the semaphore counter value is tested.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chSemObjectInit(&sem1, 0);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value />
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>A thread is created, it goes to wait on the semaphore.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+1, thread1, "A");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The semaphore counter is increased by two, it is then tested to be one, the thread must have completed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chSysLock();
|
||
|
chSemAddCounterI(&sem1, 2);
|
||
|
chSchRescheduleS();
|
||
|
chSysUnlock();
|
||
|
test_wait_threads();
|
||
|
test_assert_lock(chSemGetCounterI(&sem1) == 1, "invalid counter");
|
||
|
test_assert_sequence("A", "invalid sequence");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Testing chSemWaitSignal() functionality.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>This test case explicitly addresses the @p chSemWaitSignal() function. A thread is created that performs a wait and a signal operations. The tester thread is awakened from an atomic wait/signal operation. The test expects that the semaphore wait function returns the correct value in each of the above scenario and that the semaphore structure status is correct after each operation.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chSemObjectInit(&sem1, 0);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value><![CDATA[test_wait_threads();]]></value>
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value />
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>An higher priority thread is created that performs non-atomical wait and signal operations on a semaphore.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+1, thread3, 0);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The function chSemSignalWait() is invoked by specifying the same semaphore for the wait and signal phases. The counter value must be one on exit.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chSemSignalWait(&sem1, &sem1);
|
||
|
test_assert(queue_isempty(&sem1.queue), "queue not empty");
|
||
|
test_assert(sem1.cnt == 0, "counter not zero");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The function chSemSignalWait() is invoked again by specifying the same semaphore for the wait and signal phases. The counter value must be one on exit.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chSemSignalWait(&sem1, &sem1);
|
||
|
test_assert(queue_isempty(&sem1.queue), "queue not empty");
|
||
|
test_assert(sem1.cnt == 0, "counter not zero");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Testing Binary Semaphores special case.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>This test case tests the binary semaphores functionality. The test both checks the binary semaphore status and the expected status of the underlying counting semaphore.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value><![CDATA[test_wait_threads();]]></value>
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[binary_semaphore_t bsem;
|
||
|
msg_t msg;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Creating a binary semaphore in "taken" state, the state is checked.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chBSemObjectInit(&bsem, true);
|
||
|
test_assert_lock(chBSemGetStateI(&bsem) == true, "not taken");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Resetting the binary semaphore in "taken" state, the state must not change.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chBSemReset(&bsem, true);
|
||
|
test_assert_lock(chBSemGetStateI(&bsem) == true, "not taken");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Starting a signaler thread at a lower priority.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE,
|
||
|
chThdGetPriorityX()-1, thread4, &bsem);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Waiting for the binary semaphore to be signaled, the semaphore is expected to be taken.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[msg = chBSemWait(&bsem);
|
||
|
test_assert_lock(chBSemGetStateI(&bsem) == true, "not taken");
|
||
|
test_assert(msg == MSG_OK, "unexpected message");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Signaling the binary semaphore, checking the binary semaphore state to be "not taken" and the underlying counter semaphore counter to be one.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chBSemSignal(&bsem);
|
||
|
test_assert_lock(chBSemGetStateI(&bsem) ==false, "still taken");
|
||
|
test_assert_lock(chSemGetCounterI(&bsem.sem) == 1, "unexpected counter");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Signaling the binary semaphore again, the internal state must not change from "not taken".</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chBSemSignal(&bsem);
|
||
|
test_assert_lock(chBSemGetStateI(&bsem) == false, "taken");
|
||
|
test_assert_lock(chSemGetCounterI(&bsem.sem) == 1, "unexpected counter");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
</cases>
|
||
|
</sequence>
|
||
|
<sequence>
|
||
|
<type index="0">
|
||
|
<value>Internal Tests</value>
|
||
|
</type>
|
||
|
<brief>
|
||
|
<value>Mutexes, Condition Variables and Priority Inheritance.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>This sequence tests the ChibiOS/RT functionalities related to mutexes, condition variables and priority inheritance algorithm.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_MUTEXES</value>
|
||
|
</condition>
|
||
|
<shared_code>
|
||
|
<value><![CDATA[static MUTEX_DECL(m1);
|
||
|
static MUTEX_DECL(m2);
|
||
|
#if CH_CFG_USE_CONDVARS || defined(__DOXYGEN__)
|
||
|
static CONDVAR_DECL(c1);
|
||
|
#endif
|
||
|
|
||
|
#if CH_DBG_THREADS_PROFILING || defined(__DOXYGEN__)
|
||
|
/**
|
||
|
* @brief CPU pulse.
|
||
|
* @note The current implementation is not totally reliable.
|
||
|
*
|
||
|
* @param[in] duration CPU pulse duration in milliseconds
|
||
|
*/
|
||
|
void test_cpu_pulse(unsigned duration) {
|
||
|
systime_t start, end, now;
|
||
|
|
||
|
start = chThdGetTicksX(chThdGetSelfX());
|
||
|
end = chTimeAddX(start, TIME_MS2I(duration));
|
||
|
do {
|
||
|
now = chThdGetTicksX(chThdGetSelfX());
|
||
|
#if defined(SIMULATOR)
|
||
|
_sim_check_for_interrupts();
|
||
|
#endif
|
||
|
}
|
||
|
while (chTimeIsInRangeX(now, start, end));
|
||
|
}
|
||
|
#endif /* CH_DBG_THREADS_PROFILING */
|
||
|
|
||
|
static THD_FUNCTION(thread1, p) {
|
||
|
|
||
|
chMtxLock(&m1);
|
||
|
test_emit_token(*(char *)p);
|
||
|
chMtxUnlock(&m1);
|
||
|
}
|
||
|
|
||
|
#if CH_DBG_THREADS_PROFILING || defined(__DOXYGEN__)
|
||
|
/* Low priority thread */
|
||
|
static THD_FUNCTION(thread2L, p) {
|
||
|
|
||
|
(void)p;
|
||
|
chMtxLock(&m1);
|
||
|
test_cpu_pulse(40);
|
||
|
chMtxUnlock(&m1);
|
||
|
test_cpu_pulse(10);
|
||
|
test_emit_token('C');
|
||
|
}
|
||
|
|
||
|
/* Medium priority thread */
|
||
|
static THD_FUNCTION(thread2M, p) {
|
||
|
|
||
|
(void)p;
|
||
|
chThdSleepMilliseconds(20);
|
||
|
test_cpu_pulse(40);
|
||
|
test_emit_token('B');
|
||
|
}
|
||
|
|
||
|
/* High priority thread */
|
||
|
static THD_FUNCTION(thread2H, p) {
|
||
|
|
||
|
(void)p;
|
||
|
chThdSleepMilliseconds(40);
|
||
|
chMtxLock(&m1);
|
||
|
test_cpu_pulse(10);
|
||
|
chMtxUnlock(&m1);
|
||
|
test_emit_token('A');
|
||
|
}
|
||
|
|
||
|
/* Lowest priority thread */
|
||
|
static THD_FUNCTION(thread3LL, p) {
|
||
|
|
||
|
(void)p;
|
||
|
chMtxLock(&m1);
|
||
|
test_cpu_pulse(30);
|
||
|
chMtxUnlock(&m1);
|
||
|
test_emit_token('E');
|
||
|
}
|
||
|
|
||
|
/* Low priority thread */
|
||
|
static THD_FUNCTION(thread3L, p) {
|
||
|
|
||
|
(void)p;
|
||
|
chThdSleepMilliseconds(10);
|
||
|
chMtxLock(&m2);
|
||
|
test_cpu_pulse(20);
|
||
|
chMtxLock(&m1);
|
||
|
test_cpu_pulse(10);
|
||
|
chMtxUnlock(&m1);
|
||
|
test_cpu_pulse(10);
|
||
|
chMtxUnlock(&m2);
|
||
|
test_emit_token('D');
|
||
|
}
|
||
|
|
||
|
/* Medium priority thread */
|
||
|
static THD_FUNCTION(thread3M, p) {
|
||
|
|
||
|
(void)p;
|
||
|
chThdSleepMilliseconds(20);
|
||
|
chMtxLock(&m2);
|
||
|
test_cpu_pulse(10);
|
||
|
chMtxUnlock(&m2);
|
||
|
test_emit_token('C');
|
||
|
}
|
||
|
|
||
|
/* High priority thread */
|
||
|
static THD_FUNCTION(thread3H, p) {
|
||
|
|
||
|
(void)p;
|
||
|
chThdSleepMilliseconds(40);
|
||
|
test_cpu_pulse(20);
|
||
|
test_emit_token('B');
|
||
|
}
|
||
|
|
||
|
/* Highest priority thread */
|
||
|
static THD_FUNCTION(thread3HH, p) {
|
||
|
|
||
|
(void)p;
|
||
|
chThdSleepMilliseconds(50);
|
||
|
chMtxLock(&m2);
|
||
|
test_cpu_pulse(10);
|
||
|
chMtxUnlock(&m2);
|
||
|
test_emit_token('A');
|
||
|
}
|
||
|
#endif /* CH_DBG_THREADS_PROFILING */
|
||
|
|
||
|
static THD_FUNCTION(thread4A, p) {
|
||
|
|
||
|
(void)p;
|
||
|
chThdSleepMilliseconds(50);
|
||
|
chMtxLock(&m1);
|
||
|
chMtxUnlock(&m1);
|
||
|
}
|
||
|
|
||
|
static THD_FUNCTION(thread4B, p) {
|
||
|
|
||
|
(void)p;
|
||
|
chThdSleepMilliseconds(150);
|
||
|
chSysLock();
|
||
|
chMtxLockS(&m2); /* For coverage of the chMtxLockS() function variant.*/
|
||
|
chMtxUnlockS(&m2); /* For coverage of the chMtxUnlockS() function variant.*/
|
||
|
chSchRescheduleS();
|
||
|
chSysUnlock();
|
||
|
}
|
||
|
|
||
|
#if CH_CFG_USE_CONDVARS || defined(__DOXYGEN__)
|
||
|
static THD_FUNCTION(thread6, p) {
|
||
|
|
||
|
chMtxLock(&m1);
|
||
|
chCondWait(&c1);
|
||
|
test_emit_token(*(char *)p);
|
||
|
chMtxUnlock(&m1);
|
||
|
}
|
||
|
|
||
|
static THD_FUNCTION(thread8, p) {
|
||
|
|
||
|
chMtxLock(&m2);
|
||
|
chMtxLock(&m1);
|
||
|
#if CH_CFG_USE_CONDVARS_TIMEOUT || defined(__DOXYGEN__)
|
||
|
chCondWaitTimeout(&c1, TIME_INFINITE);
|
||
|
#else
|
||
|
chCondWait(&c1);
|
||
|
#endif
|
||
|
test_emit_token(*(char *)p);
|
||
|
chMtxUnlock(&m1);
|
||
|
chMtxUnlock(&m2);
|
||
|
}
|
||
|
|
||
|
static THD_FUNCTION(thread9, p) {
|
||
|
|
||
|
chMtxLock(&m2);
|
||
|
test_emit_token(*(char *)p);
|
||
|
chMtxUnlock(&m2);
|
||
|
}
|
||
|
#endif /* CH_CFG_USE_CONDVARS */]]></value>
|
||
|
</shared_code>
|
||
|
<cases>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Priority enqueuing test.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Five threads, with increasing priority, are enqueued on a locked mutex then the mutex is unlocked. The test expects the threads to perform their operations in increasing priority order regardless of the initial order.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chMtxObjectInit(&m1);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[tprio_t prio;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Getting the initial priority.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[prio = chThdGetPriorityX();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Locking the mutex.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chMtxLock(&m1);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Five threads are created that try to lock and unlock the mutex then terminate. The threads are created in ascending priority order.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE, prio+1, thread1, "E");
|
||
|
threads[1] = chThdCreateStatic(wa[1], WA_SIZE, prio+2, thread1, "D");
|
||
|
threads[2] = chThdCreateStatic(wa[2], WA_SIZE, prio+3, thread1, "C");
|
||
|
threads[3] = chThdCreateStatic(wa[3], WA_SIZE, prio+4, thread1, "B");
|
||
|
threads[4] = chThdCreateStatic(wa[4], WA_SIZE, prio+5, thread1, "A");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Unlocking the mutex, the threads will wakeup in priority order because the mutext queue is an ordered one.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chMtxUnlock(&m1);
|
||
|
test_wait_threads();
|
||
|
test_assert(prio == chThdGetPriorityX(), "wrong priority level");
|
||
|
test_assert_sequence("ABCDE", "invalid sequence");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Priority inheritance, simple case.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Three threads are involved in the classic priority inversion scenario, a medium priority thread tries to starve an high priority thread by blocking a low priority thread into a mutex lock zone. The test expects the threads to reach their goal in increasing priority order by rearranging their priorities in order to avoid the priority inversion trap.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_DBG_THREADS_PROFILING</value>
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chMtxObjectInit(&m1);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[systime_t time;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Getting the system time for test duration measurement.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[time = test_wait_tick();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The three contenders threads are created and let run atomically, the goals sequence is tested, the threads must complete in priority order.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-1, thread2H, 0);
|
||
|
threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-2, thread2M, 0);
|
||
|
threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-3, thread2L, 0);
|
||
|
test_wait_threads();
|
||
|
test_assert_sequence("ABC", "invalid sequence");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing that all threads completed within the specified time windows (100mS...100mS+ALLOWED_DELAY).</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_assert_time_window(chTimeAddX(time, TIME_MS2I(100)),
|
||
|
chTimeAddX(time, TIME_MS2I(100) + ALLOWED_DELAY),
|
||
|
"out of time window");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Priority inheritance, complex case.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Five threads are involved in the complex priority inversion scenario, the priority inheritance algorithm is tested for depths greater than one. The test expects the threads to perform their operations in increasing priority order by rearranging their priorities in order to avoid the priority inversion trap.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_DBG_THREADS_PROFILING</value>
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chMtxObjectInit(&m1); /* Mutex B.*/
|
||
|
chMtxObjectInit(&m2); /* Mutex A.*/]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[systime_t time;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Getting the system time for test duration measurement.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[time = test_wait_tick();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The five contenders threads are created and let run atomically, the goals sequence is tested, the threads must complete in priority order.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-5, thread3LL, 0);
|
||
|
threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-4, thread3L, 0);
|
||
|
threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-3, thread3M, 0);
|
||
|
threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()-2, thread3H, 0);
|
||
|
threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()-1, thread3HH, 0);
|
||
|
test_wait_threads();
|
||
|
test_assert_sequence("ABCDE", "invalid sequence");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing that all threads completed within the specified time windows (110mS...110mS+ALLOWED_DELAY).</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_assert_time_window(chTimeAddX(time, TIME_MS2I(110)),
|
||
|
chTimeAddX(time, TIME_MS2I(110) + ALLOWED_DELAY),
|
||
|
"out of time window");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Priority return verification.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Two threads are spawned that try to lock the mutexes already locked by the tester thread with precise timing. The test expects that the priority changes caused by the priority inheritance algorithm happen at the right moment and with the right values.<br>
|
||
|
Thread A performs wait(50), lock(m1), unlock(m1), exit. Thread B performs wait(150), lock(m2), unlock(m2), exit.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chMtxObjectInit(&m1);
|
||
|
chMtxObjectInit(&m2);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value><![CDATA[test_wait_threads();]]></value>
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[tprio_t p, pa, pb;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Getting current thread priority P(0) and assigning to the threads A and B priorities +1 and +2.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[p = chThdGetPriorityX();
|
||
|
pa = p + 1;
|
||
|
pb = p + 2;]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Spawning threads A and B at priorities P(A) and P(B).</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE, pa, thread4A, "A");
|
||
|
threads[1] = chThdCreateStatic(wa[1], WA_SIZE, pb, thread4B, "B");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Locking the mutex M1 before thread A has a chance to lock it. The priority must not change because A has not yet reached chMtxLock(M1). the mutex is not locked.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chMtxLock(&m1);
|
||
|
test_assert(chThdGetPriorityX() == p, "wrong priority level");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Waiting 100mS, this makes thread A reach chMtxLock(M1) and get the mutex. This must boost the priority of the current thread at the same level of thread A.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chThdSleepMilliseconds(100);
|
||
|
test_assert(chThdGetPriorityX() == pa, "wrong priority level");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Locking the mutex M2 before thread B has a chance to lock it. The priority must not change because B has not yet reached chMtxLock(M2). the mutex is not locked.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chMtxLock(&m2);
|
||
|
test_assert(chThdGetPriorityX() == pa, "wrong priority level");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Waiting 100mS, this makes thread B reach chMtxLock(M2) and get the mutex. This must boost the priority of the current thread at the same level of thread B.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chThdSleepMilliseconds(100);
|
||
|
test_assert(chThdGetPriorityX() == pb, "wrong priority level");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Unlocking M2, the priority should fall back to P(A).</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chMtxUnlock(&m2);
|
||
|
test_assert(chThdGetPriorityX() == pa, "wrong priority level");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Unlocking M1, the priority should fall back to P(0).</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chMtxUnlock(&m1);
|
||
|
test_assert(chThdGetPriorityX() == p, "wrong priority level");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Repeated locks, non recursive scenario.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>The behavior of multiple mutex locks from the same thread is tested when recursion is disabled</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>!CH_CFG_USE_MUTEXES_RECURSIVE</value>
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chMtxObjectInit(&m1);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[bool b;
|
||
|
tprio_t prio;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Getting current thread priority for later checks.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[prio = chThdGetPriorityX();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Locking the mutex first time, it must be possible because it is not owned.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[b = chMtxTryLock(&m1);
|
||
|
test_assert(b, "already locked");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Locking the mutex second time, it must fail because it is already owned.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[b = chMtxTryLock(&m1);
|
||
|
test_assert(!b, "not locked");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Unlocking the mutex then it must not be owned anymore and the queue must be empty.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chMtxUnlock(&m1);
|
||
|
test_assert(m1.owner == NULL, "still owned");
|
||
|
test_assert(queue_isempty(&m1.queue), "queue not empty");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing that priority has not changed after operations.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_assert(chThdGetPriorityX() == prio, "wrong priority level");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing chMtxUnlockAll() behavior.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[b = chMtxTryLock(&m1);
|
||
|
test_assert(b, "already locked");
|
||
|
b = chMtxTryLock(&m1);
|
||
|
test_assert(!b, "not locked");
|
||
|
|
||
|
chMtxUnlockAll();
|
||
|
test_assert(m1.owner == NULL, "still owned");
|
||
|
test_assert(queue_isempty(&m1.queue), "queue not empty");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing that priority has not changed after operations.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_assert(chThdGetPriorityX() == prio, "wrong priority level");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Repeated locks using, recursive scenario.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>The behavior of multiple mutex locks from the same thread is tested when recursion is enabled</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_MUTEXES_RECURSIVE</value>
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chMtxObjectInit(&m1);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[bool b;
|
||
|
tprio_t prio;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Getting current thread priority for later checks.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[prio = chThdGetPriorityX();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Locking the mutex first time, it must be possible because it is not owned.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[b = chMtxTryLock(&m1);
|
||
|
test_assert(b, "already locked");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Locking the mutex second time, it must be possible because it is recursive.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[b = chMtxTryLock(&m1);
|
||
|
test_assert(b, "already locked");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Unlocking the mutex then it must be still owned because recursivity.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chMtxUnlock(&m1);
|
||
|
test_assert(m1.owner != NULL, "not owned");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Unlocking the mutex then it must not be owned anymore and the queue must be empty.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chMtxUnlock(&m1);
|
||
|
test_assert(m1.owner == NULL, "still owned");
|
||
|
test_assert(queue_isempty(&m1.queue), "queue not empty");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing that priority has not changed after operations.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_assert(chThdGetPriorityX() == prio, "wrong priority level");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing consecutive chMtxTryLock()/chMtxTryLockS() calls and a final chMtxUnlockAllS().</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[b = chMtxTryLock(&m1);
|
||
|
test_assert(b, "already locked");
|
||
|
chSysLock();
|
||
|
b = chMtxTryLockS(&m1);
|
||
|
chSysUnlock();
|
||
|
test_assert(b, "already locked");
|
||
|
test_assert(m1.cnt == 2, "invalid recursion counter");
|
||
|
chSysLock();
|
||
|
chMtxUnlockAllS();
|
||
|
chSysUnlock();
|
||
|
test_assert(m1.owner == NULL, "still owned");
|
||
|
test_assert(queue_isempty(&m1.queue), "queue not empty");
|
||
|
test_assert(m1.cnt == 0, "invalid recursion counter");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing consecutive chMtxLock()/chMtxLockS() calls and a final chMtxUnlockAll().</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chMtxLock(&m1);
|
||
|
test_assert(m1.owner != NULL, "not owned");
|
||
|
chSysLock();
|
||
|
chMtxLockS(&m1);
|
||
|
chSysUnlock();
|
||
|
test_assert(m1.owner != NULL, "not owned");
|
||
|
test_assert(m1.cnt == 2, "invalid recursion counter");
|
||
|
chMtxUnlockAll();
|
||
|
test_assert(m1.owner == NULL, "still owned");
|
||
|
test_assert(queue_isempty(&m1.queue), "queue not empty");
|
||
|
test_assert(m1.cnt == 0, "invalid recursion counter");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing that priority has not changed after operations.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_assert(chThdGetPriorityX() == prio, "wrong priority level");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Condition Variable signal test.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Five threads take a mutex and then enter a conditional variable queue, the tester thread then proceeds to signal the conditional variable five times atomically.<br>
|
||
|
The test expects the threads to reach their goal in increasing priority order regardless of the initial order.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_CONDVARS</value>
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chCondObjectInit(&c1);
|
||
|
chMtxObjectInit(&m1);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value />
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Starting the five threads with increasing priority, the threads will queue on the condition variable.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[tprio_t prio = chThdGetPriorityX();
|
||
|
threads[0] = chThdCreateStatic(wa[0], WA_SIZE, prio+1, thread6, "E");
|
||
|
threads[1] = chThdCreateStatic(wa[1], WA_SIZE, prio+2, thread6, "D");
|
||
|
threads[2] = chThdCreateStatic(wa[2], WA_SIZE, prio+3, thread6, "C");
|
||
|
threads[3] = chThdCreateStatic(wa[3], WA_SIZE, prio+4, thread6, "B");
|
||
|
threads[4] = chThdCreateStatic(wa[4], WA_SIZE, prio+5, thread6, "A");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Atomically signaling the condition variable five times then waiting for the threads to terminate in priority order, the order is tested.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chSysLock();
|
||
|
chCondSignalI(&c1);
|
||
|
chCondSignalI(&c1);
|
||
|
chCondSignalI(&c1);
|
||
|
chCondSignalI(&c1);
|
||
|
chCondSignalI(&c1);
|
||
|
chSchRescheduleS();
|
||
|
chSysUnlock();
|
||
|
test_wait_threads();
|
||
|
test_assert_sequence("ABCDE", "invalid sequence");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Condition Variable broadcast test.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Five threads take a mutex and then enter a conditional variable queue, the tester thread then proceeds to broadcast the conditional variable.<br>
|
||
|
The test expects the threads to reach their goal in increasing priority order regardless of the initial order.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_CONDVARS</value>
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chCondObjectInit(&c1);
|
||
|
chMtxObjectInit(&m1);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value />
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Starting the five threads with increasing priority, the threads will queue on the condition variable.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[tprio_t prio = chThdGetPriorityX();
|
||
|
threads[0] = chThdCreateStatic(wa[0], WA_SIZE, prio+1, thread6, "E");
|
||
|
threads[1] = chThdCreateStatic(wa[1], WA_SIZE, prio+2, thread6, "D");
|
||
|
threads[2] = chThdCreateStatic(wa[2], WA_SIZE, prio+3, thread6, "C");
|
||
|
threads[3] = chThdCreateStatic(wa[3], WA_SIZE, prio+4, thread6, "B");
|
||
|
threads[4] = chThdCreateStatic(wa[4], WA_SIZE, prio+5, thread6, "A");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Broarcasting on the condition variable then waiting for the threads to terminate in priority order, the order is tested.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chCondBroadcast(&c1);
|
||
|
test_wait_threads();
|
||
|
test_assert_sequence("ABCDE", "invalid sequence");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Condition Variable priority boost test.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>This test case verifies the priority boost of a thread waiting on a conditional variable queue. It tests this very specific situation in order to improve code coverage. The created threads perform the following operations: TA{lock(M2), lock(M1), wait(C1), unlock(M1), unlock(M2)}, TB{lock(M2), wait(C1), unlock(M2)}. TC{lock(M1), unlock(M1)}.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_CONDVARS</value>
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chCondObjectInit(&c1);
|
||
|
chMtxObjectInit(&m1);
|
||
|
chMtxObjectInit(&m2);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[tprio_t prio;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Reading current base priority.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[prio = chThdGetPriorityX();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Thread A is created at priority P(+1), it locks M2, locks M1 and goes to wait on C1.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE, prio+1, thread8, "A");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Thread C is created at priority P(+2), it enqueues on M1 and boosts TA priority at P(+2).</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[1] = chThdCreateStatic(wa[1], WA_SIZE, prio+2, thread6, "C");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Thread B is created at priority P(+3), it enqueues on M2 and boosts TA priority at P(+3).</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[2] = chThdCreateStatic(wa[2], WA_SIZE, prio+3, thread9, "B");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Signaling C1: TA wakes up, unlocks M1 and priority goes to P(+2). TB locks M1, unlocks M1 and completes. TA unlocks M2 and priority goes to P(+1). TC waits on C1. TA completes.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chCondSignal(&c1);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Signaling C1: TC wakes up, unlocks M1 and completes.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chCondSignal(&c1);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Checking the order of operations.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_wait_threads();
|
||
|
test_assert_sequence("ABC", "invalid sequence");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
</cases>
|
||
|
</sequence>
|
||
|
<sequence>
|
||
|
<type index="0">
|
||
|
<value>Internal Tests</value>
|
||
|
</type>
|
||
|
<brief>
|
||
|
<value>Synchronous Messages.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>This module implements the test sequence for the Synchronous Messages subsystem.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_MESSAGES</value>
|
||
|
</condition>
|
||
|
<shared_code>
|
||
|
<value><![CDATA[static THD_FUNCTION(msg_thread1, p) {
|
||
|
|
||
|
chMsgSend(p, 'A');
|
||
|
chMsgSend(p, 'B');
|
||
|
chMsgSend(p, 'C');
|
||
|
chMsgSend(p, 'D');
|
||
|
}]]></value>
|
||
|
</shared_code>
|
||
|
<cases>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Messages Server loop.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>A messenger thread is spawned that sends four messages back to the tester thread.<br>
|
||
|
The test expect to receive the messages in the correct sequence and to not find a fifth message waiting.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[thread_t *tp;
|
||
|
msg_t msg;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Starting the messenger thread.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX() + 1,
|
||
|
msg_thread1, chThdGetSelfX());]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Waiting for four messages then testing the receive order.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[unsigned i;
|
||
|
|
||
|
for (i = 0; i < 4; i++) {
|
||
|
tp = chMsgWait();
|
||
|
msg = chMsgGet(tp);
|
||
|
chMsgRelease(tp, msg);
|
||
|
test_emit_token(msg);
|
||
|
}
|
||
|
test_wait_threads();
|
||
|
test_assert_sequence("ABCD", "invalid sequence");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
</cases>
|
||
|
</sequence>
|
||
|
<sequence>
|
||
|
<type index="0">
|
||
|
<value>Internal Tests</value>
|
||
|
</type>
|
||
|
<brief>
|
||
|
<value>Event Sources and Event Flags.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>This module implements the test sequence for the Events subsystem.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_EVENTS</value>
|
||
|
</condition>
|
||
|
<shared_code>
|
||
|
<value><![CDATA[static EVENTSOURCE_DECL(es1);
|
||
|
static EVENTSOURCE_DECL(es2);
|
||
|
|
||
|
static void h1(eventid_t id) {(void)id;test_emit_token('A');}
|
||
|
static void h2(eventid_t id) {(void)id;test_emit_token('B');}
|
||
|
static void h3(eventid_t id) {(void)id;test_emit_token('C');}
|
||
|
static ROMCONST evhandler_t evhndl[] = {h1, h2, h3};
|
||
|
|
||
|
static THD_FUNCTION(evt_thread3, p) {
|
||
|
|
||
|
chThdSleepMilliseconds(50);
|
||
|
chEvtSignal((thread_t *)p, 1);
|
||
|
}
|
||
|
|
||
|
static THD_FUNCTION(evt_thread7, p) {
|
||
|
|
||
|
(void)p;
|
||
|
chEvtBroadcast(&es1);
|
||
|
chThdSleepMilliseconds(50);
|
||
|
chEvtBroadcast(&es2);
|
||
|
}]]></value>
|
||
|
</shared_code>
|
||
|
<cases>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Events registration.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Two event listeners are registered on an event source and then unregistered in the same order.<br>
|
||
|
The test expects that the even source has listeners after the registrations and after the first unregistration, then, after the second unegistration, the test expects no more listeners.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[event_listener_t el1, el2;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>An Event Source is initialized.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chEvtObjectInit(&es1);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Two Event Listeners are registered on the Event Source, the Event Source is tested to have listeners.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chEvtRegisterMask(&es1, &el1, 1);
|
||
|
chEvtRegisterMask(&es1, &el2, 2);
|
||
|
test_assert_lock(chEvtIsListeningI(&es1), "no listener");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>An Event Listener is unregistered, the Event Source must still have listeners.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chEvtUnregister(&es1, &el1);
|
||
|
test_assert_lock(chEvtIsListeningI(&es1), "no listener");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>An Event Listener is unregistered, the Event Source must not have listeners.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chEvtUnregister(&es1, &el2);
|
||
|
test_assert_lock(!chEvtIsListeningI(&es1), "stuck listener");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Event Flags dispatching.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>The test dispatches three event flags and verifies that the associated event handlers are invoked in LSb-first order.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chEvtGetAndClearEvents(ALL_EVENTS);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value />
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Three evenf flag bits are raised then chEvtDispatch() is invoked, the sequence of handlers calls is tested.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chEvtDispatch(evhndl, 7);
|
||
|
test_assert_sequence("ABC", "invalid sequence");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Events Flags wait using chEvtWaitOne().</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Functionality of chEvtWaitOne() is tested under various scenarios.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chEvtGetAndClearEvents(ALL_EVENTS);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[eventmask_t m;
|
||
|
systime_t target_time;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Setting three event flags.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chEvtAddEvents(7);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Calling chEvtWaitOne() three times, each time a single flag must be returned in order of priority.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[m = chEvtWaitOne(ALL_EVENTS);
|
||
|
test_assert(m == 1, "single event error");
|
||
|
m = chEvtWaitOne(ALL_EVENTS);
|
||
|
test_assert(m == 2, "single event error");
|
||
|
m = chEvtWaitOne(ALL_EVENTS);
|
||
|
test_assert(m == 4, "single event error");
|
||
|
m = chEvtGetAndClearEvents(ALL_EVENTS);
|
||
|
test_assert(m == 0, "stuck event");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Getting current time and starting a signaler thread, the thread will set an event flag after 50mS.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[target_time = chTimeAddX(test_wait_tick(), TIME_MS2I(50));
|
||
|
threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX() - 1,
|
||
|
evt_thread3, chThdGetSelfX());]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Calling chEvtWaitOne() then verifying that the event has been received after 50mS and that the event flags mask has been emptied.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[m = chEvtWaitOne(ALL_EVENTS);
|
||
|
test_assert_time_window(target_time,
|
||
|
chTimeAddX(target_time, ALLOWED_DELAY),
|
||
|
"out of time window");
|
||
|
test_assert(m == 1, "event flag error");
|
||
|
m = chEvtGetAndClearEvents(ALL_EVENTS);
|
||
|
test_assert(m == 0, "stuck event");
|
||
|
test_wait_threads();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Events Flags wait using chEvtWaitAny().</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Functionality of chEvtWaitAny() is tested under various scenarios.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chEvtGetAndClearEvents(ALL_EVENTS);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[eventmask_t m;
|
||
|
systime_t target_time;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Setting two, non contiguous, event flags.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chEvtAddEvents(5);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Calling chEvtWaitAny() one time, the two flags must be returned.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[m = chEvtWaitAny(ALL_EVENTS);
|
||
|
test_assert(m == 5, "unexpected pending bit");
|
||
|
m = chEvtGetAndClearEvents(ALL_EVENTS);
|
||
|
test_assert(m == 0, "stuck event");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Getting current time and starting a signaler thread, the thread will set an event flag after 50mS.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[target_time = chTimeAddX(test_wait_tick(), TIME_MS2I(50));
|
||
|
threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX() - 1,
|
||
|
evt_thread3, chThdGetSelfX());]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Calling chEvtWaitAny() then verifying that the event has been received after 50mS and that the event flags mask has been emptied.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[m = chEvtWaitAny(ALL_EVENTS);
|
||
|
test_assert_time_window(target_time,
|
||
|
chTimeAddX(target_time, ALLOWED_DELAY),
|
||
|
"out of time window");
|
||
|
test_assert(m == 1, "event flag error");
|
||
|
m = chEvtGetAndClearEvents(ALL_EVENTS);
|
||
|
test_assert(m == 0, "stuck event");
|
||
|
test_wait_threads();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Events Flags wait using chEvtWaitAll().</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Functionality of chEvtWaitAll() is tested under various scenarios.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chEvtGetAndClearEvents(ALL_EVENTS);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[eventmask_t m;
|
||
|
systime_t target_time;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Setting two, non contiguous, event flags.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chEvtAddEvents(5);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Calling chEvtWaitAll() one time, the two flags must be returned.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[m = chEvtWaitAll(5);
|
||
|
test_assert(m == 5, "unexpected pending bit");
|
||
|
m = chEvtGetAndClearEvents(ALL_EVENTS);
|
||
|
test_assert(m == 0, "stuck event");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Setting one event flag.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chEvtAddEvents(4);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Getting current time and starting a signaler thread, the thread will set another event flag after 50mS.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[target_time = chTimeAddX(test_wait_tick(), TIME_MS2I(50));
|
||
|
threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX() - 1,
|
||
|
evt_thread3, chThdGetSelfX());]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Calling chEvtWaitAll() then verifying that both event flags have been received after 50mS and that the event flags mask has been emptied.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[m = chEvtWaitAll(5);
|
||
|
test_assert_time_window(target_time,
|
||
|
chTimeAddX(target_time, ALLOWED_DELAY),
|
||
|
"out of time window");
|
||
|
test_assert(m == 5, "event flags error");
|
||
|
m = chEvtGetAndClearEvents(ALL_EVENTS);
|
||
|
test_assert(m == 0, "stuck event");
|
||
|
test_wait_threads();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Events Flags wait timeouts.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Timeout functionality is tested for chEvtWaitOneTimeout(), chEvtWaitAnyTimeout() and chEvtWaitAllTimeout().</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_EVENTS_TIMEOUT</value>
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chEvtGetAndClearEvents(ALL_EVENTS);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[eventmask_t m;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The functions are invoked first with TIME_IMMEDIATE timeout, the timeout condition is tested.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[m = chEvtWaitOneTimeout(ALL_EVENTS, TIME_IMMEDIATE);
|
||
|
test_assert(m == 0, "spurious event");
|
||
|
m = chEvtWaitAnyTimeout(ALL_EVENTS, TIME_IMMEDIATE);
|
||
|
test_assert(m == 0, "spurious event");
|
||
|
m = chEvtWaitAllTimeout(ALL_EVENTS, TIME_IMMEDIATE);
|
||
|
test_assert(m == 0, "spurious event");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The functions are invoked first with a 50mS timeout, the timeout condition is tested.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[m = chEvtWaitOneTimeout(ALL_EVENTS, TIME_MS2I(50));
|
||
|
test_assert(m == 0, "spurious event");
|
||
|
m = chEvtWaitAnyTimeout(ALL_EVENTS, TIME_MS2I(50));
|
||
|
test_assert(m == 0, "spurious event");
|
||
|
m = chEvtWaitAllTimeout(ALL_EVENTS, TIME_MS2I(50));
|
||
|
test_assert(m == 0, "spurious event");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Broadcasting using chEvtBroadcast().</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Functionality of chEvtBroadcast() is tested.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chEvtGetAndClearEvents(ALL_EVENTS);
|
||
|
chEvtObjectInit(&es1);
|
||
|
chEvtObjectInit(&es2);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[eventmask_t m;
|
||
|
event_listener_t el1, el2;
|
||
|
systime_t target_time;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Registering on two event sources associating them with flags 1 and 4.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chEvtRegisterMask(&es1, &el1, 1);
|
||
|
chEvtRegisterMask(&es2, &el2, 4);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Getting current time and starting a broadcaster thread, the thread broadcast the first Event Source immediately and the other after 50mS.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[target_time = chTimeAddX(test_wait_tick(), TIME_MS2I(50));
|
||
|
threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX() - 1,
|
||
|
evt_thread7, "A");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Calling chEvtWaitAll() then verifying that both event flags have been received after 50mS and that the event flags mask has been emptied.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[m = chEvtWaitAll(5);
|
||
|
test_assert_time_window(target_time,
|
||
|
chTimeAddX(target_time, ALLOWED_DELAY),
|
||
|
"out of time window");
|
||
|
m = chEvtGetAndClearEvents(ALL_EVENTS);
|
||
|
test_assert(m == 0, "stuck event");
|
||
|
test_wait_threads();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Unregistering from the Event Sources.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chEvtUnregister(&es1, &el1);
|
||
|
chEvtUnregister(&es2, &el2);
|
||
|
test_assert(!chEvtIsListeningI(&es1), "stuck listener");
|
||
|
test_assert(!chEvtIsListeningI(&es2), "stuck listener");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
</cases>
|
||
|
</sequence>
|
||
|
<sequence>
|
||
|
<type index="0">
|
||
|
<value>Internal Tests</value>
|
||
|
</type>
|
||
|
<brief>
|
||
|
<value>Dynamic threads.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>This module implements the test sequence for the dynamic thread creation APIs.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_DYNAMIC</value>
|
||
|
</condition>
|
||
|
<shared_code>
|
||
|
<value><![CDATA[#if CH_CFG_USE_HEAP
|
||
|
static memory_heap_t heap1;
|
||
|
#endif
|
||
|
#if CH_CFG_USE_MEMPOOLS
|
||
|
static memory_pool_t mp1;
|
||
|
#endif
|
||
|
|
||
|
static THD_FUNCTION(dyn_thread1, p) {
|
||
|
|
||
|
test_emit_token(*(char *)p);
|
||
|
}]]></value>
|
||
|
</shared_code>
|
||
|
<cases>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Threads creation from Memory Heap.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Two threads are started by allocating the memory from the Memory Heap then a third thread is started with a huge stack requirement.<br>
|
||
|
The test expects the first two threads to successfully start and the third one to fail.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_HEAP</value>
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chHeapObjectInit(&heap1, test_buffer, sizeof test_buffer);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[size_t n1, total1, largest1;
|
||
|
size_t n2, total2, largest2;
|
||
|
tprio_t prio;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Getting base priority for threads.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[prio = chThdGetPriorityX();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Getting heap info before the test.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[n1 = chHeapStatus(&heap1, &total1, &largest1);
|
||
|
test_assert(n1 == 1, "heap fragmented");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Creating thread 1, it is expected to succeed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateFromHeap(&heap1,
|
||
|
THD_WORKING_AREA_SIZE(THREADS_STACK_SIZE),
|
||
|
"dyn1",
|
||
|
prio-1, dyn_thread1, "A");
|
||
|
test_assert(threads[0] != NULL, "thread creation failed");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Creating thread 2, it is expected to succeed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[1] = chThdCreateFromHeap(&heap1,
|
||
|
THD_WORKING_AREA_SIZE(THREADS_STACK_SIZE),
|
||
|
"dyn2",
|
||
|
prio-2, dyn_thread1, "B");
|
||
|
test_assert(threads[1] != NULL, "thread creation failed");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Creating thread 3, it is expected to fail</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[2] = chThdCreateFromHeap(&heap1,
|
||
|
(((size_t)-1) >> 1U) + 1U,
|
||
|
"dyn3",
|
||
|
prio-3, dyn_thread1, "C");
|
||
|
test_assert(threads[2] == NULL, "thread creation not failed");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Letting threads execute then checking the start order and freeing memory.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_wait_threads();
|
||
|
test_assert_sequence("AB", "invalid sequence");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Getting heap info again for verification.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[n2 = chHeapStatus(&heap1, &total2, &largest2);
|
||
|
test_assert(n1 == n2, "fragmentation changed");
|
||
|
test_assert(total1 == total2, "total free space changed");
|
||
|
test_assert(largest1 == largest2, "largest fragment size changed");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Threads creation from Memory Pool.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Five thread creation are attempted from a pool containing only four elements.<br>
|
||
|
The test expects the first four threads to successfully start and the last one to fail.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_MEMPOOLS</value>
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chPoolObjectInit(&mp1, THD_WORKING_AREA_SIZE(THREADS_STACK_SIZE), NULL);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[unsigned i;
|
||
|
tprio_t prio;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Adding four working areas to the pool.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[for (i = 0; i < 4; i++)
|
||
|
chPoolFree(&mp1, wa[i]);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Getting base priority for threads.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[prio = chThdGetPriorityX();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Creating the five threads.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateFromMemoryPool(&mp1, "dyn1", prio-1, dyn_thread1, "A");
|
||
|
threads[1] = chThdCreateFromMemoryPool(&mp1, "dyn2", prio-2, dyn_thread1, "B");
|
||
|
threads[2] = chThdCreateFromMemoryPool(&mp1, "dyn3", prio-3, dyn_thread1, "C");
|
||
|
threads[3] = chThdCreateFromMemoryPool(&mp1, "dyn4", prio-4, dyn_thread1, "D");
|
||
|
threads[4] = chThdCreateFromMemoryPool(&mp1, "dyn5", prio-5, dyn_thread1, "E");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing that only the fifth thread creation failed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_assert((threads[0] != NULL) &&
|
||
|
(threads[1] != NULL) &&
|
||
|
(threads[2] != NULL) &&
|
||
|
(threads[3] != NULL),
|
||
|
"thread creation failed");
|
||
|
test_assert(threads[4] == NULL,
|
||
|
"thread creation not failed");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Letting them run, free the memory then checking the execution sequence.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_wait_threads();
|
||
|
test_assert_sequence("ABCD", "invalid sequence");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Testing that the pool contains four elements again.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[for (i = 0; i < 4; i++)
|
||
|
test_assert(chPoolAlloc(&mp1) != NULL, "pool list empty");
|
||
|
test_assert(chPoolAlloc(&mp1) == NULL, "pool list not empty");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
</cases>
|
||
|
</sequence>
|
||
|
<sequence>
|
||
|
<type index="2">
|
||
|
<value>Benchmarks</value>
|
||
|
</type>
|
||
|
<brief>
|
||
|
<value>Benchmarks.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>This module implements a series of system benchmarks. The benchmarks are useful as a stress test and as a reference when comparing ChibiOS/RT with similar systems.<br>
|
||
|
Objective of the test sequence is to provide a performance index for the most critical system subsystems. The performance numbers allow to discover performance regressions between successive ChibiOS/RT releases.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<shared_code>
|
||
|
<value><![CDATA[#if CH_CFG_USE_SEMAPHORES || defined(__DOXYGEN__)
|
||
|
static semaphore_t sem1;
|
||
|
#endif
|
||
|
#if CH_CFG_USE_MUTEXES || defined(__DOXYGEN__)
|
||
|
static mutex_t mtx1;
|
||
|
#endif
|
||
|
|
||
|
static void tmo(void *param) {(void)param;}
|
||
|
|
||
|
#if CH_CFG_USE_MESSAGES
|
||
|
static THD_FUNCTION(bmk_thread1, p) {
|
||
|
thread_t *tp;
|
||
|
msg_t msg;
|
||
|
|
||
|
(void)p;
|
||
|
do {
|
||
|
tp = chMsgWait();
|
||
|
msg = chMsgGet(tp);
|
||
|
chMsgRelease(tp, msg);
|
||
|
} while (msg);
|
||
|
}
|
||
|
|
||
|
NOINLINE static unsigned int msg_loop_test(thread_t *tp) {
|
||
|
systime_t start, end;
|
||
|
|
||
|
uint32_t n = 0;
|
||
|
start = test_wait_tick();
|
||
|
end = chTimeAddX(start, TIME_MS2I(1000));
|
||
|
do {
|
||
|
(void)chMsgSend(tp, 1);
|
||
|
n++;
|
||
|
#if defined(SIMULATOR)
|
||
|
_sim_check_for_interrupts();
|
||
|
#endif
|
||
|
} while (chVTIsSystemTimeWithinX(start, end));
|
||
|
(void)chMsgSend(tp, 0);
|
||
|
return n;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static THD_FUNCTION(bmk_thread3, p) {
|
||
|
|
||
|
chThdExit((msg_t)p);
|
||
|
}
|
||
|
|
||
|
static THD_FUNCTION(bmk_thread4, p) {
|
||
|
msg_t msg;
|
||
|
thread_t *self = chThdGetSelfX();
|
||
|
|
||
|
(void)p;
|
||
|
chSysLock();
|
||
|
do {
|
||
|
chSchGoSleepS(CH_STATE_SUSPENDED);
|
||
|
msg = self->u.rdymsg;
|
||
|
} while (msg == MSG_OK);
|
||
|
chSysUnlock();
|
||
|
}
|
||
|
|
||
|
#if CH_CFG_USE_SEMAPHORES
|
||
|
static THD_FUNCTION(bmk_thread7, p) {
|
||
|
|
||
|
(void)p;
|
||
|
while (!chThdShouldTerminateX())
|
||
|
chSemWait(&sem1);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static THD_FUNCTION(bmk_thread8, p) {
|
||
|
|
||
|
do {
|
||
|
chThdYield();
|
||
|
chThdYield();
|
||
|
chThdYield();
|
||
|
chThdYield();
|
||
|
(*(uint32_t *)p) += 4;
|
||
|
#if defined(SIMULATOR)
|
||
|
_sim_check_for_interrupts();
|
||
|
#endif
|
||
|
} while(!chThdShouldTerminateX());
|
||
|
}]]></value>
|
||
|
</shared_code>
|
||
|
<cases>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Messages performance #1.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>A message server thread is created with a lower priority than the client thread, the messages throughput per second is measured and the result printed on the output log.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_MESSAGES</value>
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[uint32_t n;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The messenger thread is started at a lower priority than the current thread.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-1, bmk_thread1, NULL);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The number of messages exchanged is counted in a one second time window.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[n = msg_loop_test(threads[0]);
|
||
|
test_wait_threads();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Score is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_print("--- Score : ");
|
||
|
test_printn(n);
|
||
|
test_print(" msgs/S, ");
|
||
|
test_printn(n << 1);
|
||
|
test_println(" ctxswc/S");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Messages performance #2.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>A message server thread is created with an higher priority than the client thread, the messages throughput per second is measured and the result printed on the output log.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_MESSAGES</value>
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[uint32_t n;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The messenger thread is started at an higher priority than the current thread.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+1, bmk_thread1, NULL);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The number of messages exchanged is counted in a one second time window.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[n = msg_loop_test(threads[0]);
|
||
|
test_wait_threads();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Score is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_print("--- Score : ");
|
||
|
test_printn(n);
|
||
|
test_print(" msgs/S, ");
|
||
|
test_printn(n << 1);
|
||
|
test_println(" ctxswc/S");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Messages performance #3.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>A message server thread is created with an higher priority than the client thread, four lower priority threads crowd the ready list, the messages throughput per second is measured while the ready list and the result printed on the output log.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_MESSAGES</value>
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[uint32_t n;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The messenger thread is started at an higher priority than the current thread.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+1, bmk_thread1, NULL);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Four threads are started at a lower priority than the current thread.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-2, bmk_thread3, NULL);
|
||
|
threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-3, bmk_thread3, NULL);
|
||
|
threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()-4, bmk_thread3, NULL);
|
||
|
threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()-5, bmk_thread3, NULL);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The number of messages exchanged is counted in a one second time window.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[n = msg_loop_test(threads[0]);
|
||
|
test_wait_threads();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Score is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_print("--- Score : ");
|
||
|
test_printn(n);
|
||
|
test_print(" msgs/S, ");
|
||
|
test_printn(n << 1);
|
||
|
test_println(" ctxswc/S");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Context Switch performance.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>A thread is created that just performs a @p chSchGoSleepS() into a loop, the thread is awakened as fast is possible by the tester thread.<br>
|
||
|
The Context Switch performance is calculated by measuring the number of iterations after a second of continuous operations.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[thread_t *tp;
|
||
|
uint32_t n;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Starting the target thread at an higher priority level.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[tp = threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+1,
|
||
|
bmk_thread4, NULL);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Waking up the thread as fast as possible in a one second time window.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[systime_t start, end;
|
||
|
|
||
|
n = 0;
|
||
|
start = test_wait_tick();
|
||
|
end = chTimeAddX(start, TIME_MS2I(1000));
|
||
|
do {
|
||
|
chSysLock();
|
||
|
chSchWakeupS(tp, MSG_OK);
|
||
|
chSchWakeupS(tp, MSG_OK);
|
||
|
chSchWakeupS(tp, MSG_OK);
|
||
|
chSchWakeupS(tp, MSG_OK);
|
||
|
chSysUnlock();
|
||
|
n += 4;
|
||
|
#if defined(SIMULATOR)
|
||
|
_sim_check_for_interrupts();
|
||
|
#endif
|
||
|
} while (chVTIsSystemTimeWithinX(start, end));]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Stopping the target thread.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chSysLock();
|
||
|
chSchWakeupS(tp, MSG_TIMEOUT);
|
||
|
chSysUnlock();
|
||
|
test_wait_threads();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Score is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_print("--- Score : ");
|
||
|
test_printn(n * 2);
|
||
|
test_println(" ctxswc/S");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Threads performance, full cycle.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Threads are continuously created and terminated into a loop. A full chThdCreateStatic() / @p chThdExit() / @p chThdWait() cycle is performed in each iteration.<br>
|
||
|
The performance is calculated by measuring the number of iterations after a second of continuous operations.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[uint32_t n;
|
||
|
tprio_t prio = chThdGetPriorityX() - 1;
|
||
|
systime_t start, end;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>A thread is created at a lower priority level and its termination detected using @p chThdWait(). The operation is repeated continuously in a one-second time window.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[n = 0;
|
||
|
start = test_wait_tick();
|
||
|
end = chTimeAddX(start, TIME_MS2I(1000));
|
||
|
do {
|
||
|
chThdWait(chThdCreateStatic(wa[0], WA_SIZE, prio, bmk_thread3, NULL));
|
||
|
n++;
|
||
|
#if defined(SIMULATOR)
|
||
|
_sim_check_for_interrupts();
|
||
|
#endif
|
||
|
} while (chVTIsSystemTimeWithinX(start, end));]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Score is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_print("--- Score : ");
|
||
|
test_printn(n);
|
||
|
test_println(" threads/S");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Threads performance, create/exit only.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Threads are continuously created and terminated into a loop. A partial @p chThdCreateStatic() / @p chThdExit() cycle is performed in each iteration, the @p chThdWait() is not necessary because the thread is created at an higher priority so there is no need to wait for it to terminate.<br> The performance is calculated by measuring the number of iterations after a second of continuous operations.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[uint32_t n;
|
||
|
tprio_t prio = chThdGetPriorityX() + 1;
|
||
|
systime_t start, end;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>A thread is created at an higher priority level and let terminate immediately. The operation is repeated continuously in a one-second time window.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[n = 0;
|
||
|
start = test_wait_tick();
|
||
|
end = chTimeAddX(start, TIME_MS2I(1000));
|
||
|
do {
|
||
|
#if CH_CFG_USE_REGISTRY
|
||
|
chThdRelease(chThdCreateStatic(wa[0], WA_SIZE, prio, bmk_thread3, NULL));
|
||
|
#else
|
||
|
chThdCreateStatic(wa[0], WA_SIZE, prio, bmk_thread3, NULL);
|
||
|
#endif
|
||
|
n++;
|
||
|
#if defined(SIMULATOR)
|
||
|
_sim_check_for_interrupts();
|
||
|
#endif
|
||
|
} while (chVTIsSystemTimeWithinX(start, end));]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Score is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_print("--- Score : ");
|
||
|
test_printn(n);
|
||
|
test_println(" threads/S");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Mass reschedule performance.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Five threads are created and atomically rescheduled by resetting the semaphore where they are waiting on. The operation is performed into a continuous loop.<br>
|
||
|
The performance is calculated by measuring the number of iterations after a second of continuous operations.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_SEMAPHORES</value>
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chSemObjectInit(&sem1, 0);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[uint32_t n;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Five threads are created at higher priority that immediately enqueue on a semaphore.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()+5, bmk_thread7, NULL);
|
||
|
threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()+4, bmk_thread7, NULL);
|
||
|
threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()+3, bmk_thread7, NULL);
|
||
|
threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()+2, bmk_thread7, NULL);
|
||
|
threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()+1, bmk_thread7, NULL);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The semaphore is reset waking up the five threads. The operation is repeated continuously in a one-second time window.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[systime_t start, end;
|
||
|
|
||
|
n = 0;
|
||
|
start = test_wait_tick();
|
||
|
end = chTimeAddX(start, TIME_MS2I(1000));
|
||
|
do {
|
||
|
chSemReset(&sem1, 0);
|
||
|
n++;
|
||
|
#if defined(SIMULATOR)
|
||
|
_sim_check_for_interrupts();
|
||
|
#endif
|
||
|
} while (chVTIsSystemTimeWithinX(start, end));]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The five threads are terminated.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_terminate_threads();
|
||
|
chSemReset(&sem1, 0);
|
||
|
test_wait_threads();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The score is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_print("--- Score : ");
|
||
|
test_printn(n);
|
||
|
test_print(" reschedules/S, ");
|
||
|
test_printn(n * 6);
|
||
|
test_println(" ctxswc/S");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Round-Robin voluntary reschedule.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>Five threads are created at equal priority, each thread just increases a variable and yields.<br>
|
||
|
The performance is calculated by measuring the number of iterations after a second of continuous operations.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[uint32_t n;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The five threads are created at lower priority. The threds have equal priority and start calling @p chThdYield() continuously.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[n = 0;
|
||
|
test_wait_tick();threads[0] = chThdCreateStatic(wa[0], WA_SIZE, chThdGetPriorityX()-1, bmk_thread8, (void *)&n);
|
||
|
|
||
|
threads[1] = chThdCreateStatic(wa[1], WA_SIZE, chThdGetPriorityX()-1, bmk_thread8, (void *)&n);
|
||
|
threads[2] = chThdCreateStatic(wa[2], WA_SIZE, chThdGetPriorityX()-1, bmk_thread8, (void *)&n);
|
||
|
threads[3] = chThdCreateStatic(wa[3], WA_SIZE, chThdGetPriorityX()-1, bmk_thread8, (void *)&n);
|
||
|
threads[4] = chThdCreateStatic(wa[4], WA_SIZE, chThdGetPriorityX()-1, bmk_thread8, (void *)&n);]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Waiting one second then terminating the 5 threads.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[chThdSleepSeconds(1);
|
||
|
test_terminate_threads();
|
||
|
test_wait_threads();]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The score is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_print("--- Score : ");
|
||
|
test_printn(n);
|
||
|
test_println(" ctxswc/S");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Virtual Timers set/reset performance.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>A virtual timer is set and immediately reset into a continuous loop.<br>
|
||
|
The performance is calculated by measuring the number of iterations after a second of continuous operations.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[static virtual_timer_t vt1, vt2;
|
||
|
uint32_t n;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>Two timers are set then reset without waiting for their counter to elapse. The operation is repeated continuously in a one-second time window.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[systime_t start, end;
|
||
|
|
||
|
n = 0;
|
||
|
start = test_wait_tick();
|
||
|
end = chTimeAddX(start, TIME_MS2I(1000));
|
||
|
do {
|
||
|
chSysLock();
|
||
|
chVTDoSetI(&vt1, 1, tmo, NULL);
|
||
|
chVTDoSetI(&vt2, 10000, tmo, NULL);
|
||
|
chVTDoResetI(&vt1);
|
||
|
chVTDoResetI(&vt2);
|
||
|
chSysUnlock();
|
||
|
n++;
|
||
|
#if defined(SIMULATOR)
|
||
|
_sim_check_for_interrupts();
|
||
|
#endif
|
||
|
} while (chVTIsSystemTimeWithinX(start, end));]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The score is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_print("--- Score : ");
|
||
|
test_printn(n * 2);
|
||
|
test_println(" timers/S");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Semaphores wait/signal performance</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>A counting semaphore is taken/released into a continuous loop, no Context Switch happens because the counter is always non negative.<br>
|
||
|
The performance is calculated by measuring the number of iterations after a second of continuous operations.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_SEMAPHORES</value>
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chSemObjectInit(&sem1, 1);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[uint32_t n;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>A semaphore is teken and released. The operation is repeated continuously in a one-second time window.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[systime_t start, end;
|
||
|
|
||
|
n = 0;
|
||
|
start = test_wait_tick();
|
||
|
end = chTimeAddX(start, TIME_MS2I(1000));
|
||
|
do {
|
||
|
chSemWait(&sem1);
|
||
|
chSemSignal(&sem1);
|
||
|
chSemWait(&sem1);
|
||
|
chSemSignal(&sem1);
|
||
|
chSemWait(&sem1);
|
||
|
chSemSignal(&sem1);
|
||
|
chSemWait(&sem1);
|
||
|
chSemSignal(&sem1);
|
||
|
n++;
|
||
|
#if defined(SIMULATOR)
|
||
|
_sim_check_for_interrupts();
|
||
|
#endif
|
||
|
} while (chVTIsSystemTimeWithinX(start, end));]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The score is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_print("--- Score : ");
|
||
|
test_printn(n * 4);
|
||
|
test_println(" wait+signal/S");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>Mutexes lock/unlock performance</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>A mutex is locked/unlocked into a continuous loop, no Context Switch happens because there are no other threads asking for the mutex.<br>
|
||
|
The performance is calculated by measuring the number of iterations after a second of continuous operations.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value>CH_CFG_USE_MUTEXES</value>
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value><![CDATA[chMtxObjectInit(&mtx1);]]></value>
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value><![CDATA[uint32_t n;]]></value>
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>A mutex is locked and unlocked. The operation is repeated continuously in a one-second time window.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[systime_t start, end;
|
||
|
|
||
|
n = 0;
|
||
|
start = test_wait_tick();
|
||
|
end = chTimeAddX(start, TIME_MS2I(1000));
|
||
|
do {
|
||
|
chMtxLock(&mtx1);
|
||
|
chMtxUnlock(&mtx1);
|
||
|
chMtxLock(&mtx1);
|
||
|
chMtxUnlock(&mtx1);
|
||
|
chMtxLock(&mtx1);
|
||
|
chMtxUnlock(&mtx1);
|
||
|
chMtxLock(&mtx1);
|
||
|
chMtxUnlock(&mtx1);
|
||
|
n++;
|
||
|
#if defined(SIMULATOR)
|
||
|
_sim_check_for_interrupts();
|
||
|
#endif
|
||
|
} while (chVTIsSystemTimeWithinX(start, end));]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The score is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_print("--- Score : ");
|
||
|
test_printn(n * 4);
|
||
|
test_println(" lock+unlock/S");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
<case>
|
||
|
<brief>
|
||
|
<value>RAM Footprint.</value>
|
||
|
</brief>
|
||
|
<description>
|
||
|
<value>The memory size of the various kernel objects is printed.</value>
|
||
|
</description>
|
||
|
<condition>
|
||
|
<value />
|
||
|
</condition>
|
||
|
<various_code>
|
||
|
<setup_code>
|
||
|
<value />
|
||
|
</setup_code>
|
||
|
<teardown_code>
|
||
|
<value />
|
||
|
</teardown_code>
|
||
|
<local_variables>
|
||
|
<value />
|
||
|
</local_variables>
|
||
|
</various_code>
|
||
|
<steps>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The size of the system area is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_print("--- System: ");
|
||
|
test_printn(sizeof(ch_system_t));
|
||
|
test_println(" bytes");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The size of a thread structure is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_print("--- Thread: ");
|
||
|
test_printn(sizeof(thread_t));
|
||
|
test_println(" bytes");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The size of a virtual timer structure is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[test_print("--- Timer : ");
|
||
|
test_printn(sizeof(virtual_timer_t));
|
||
|
test_println(" bytes");]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The size of a semaphore structure is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[#if CH_CFG_USE_SEMAPHORES || defined(__DOXYGEN__)
|
||
|
test_print("--- Semaph: ");
|
||
|
test_printn(sizeof(semaphore_t));
|
||
|
test_println(" bytes");
|
||
|
#endif]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The size of a mutex is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[#if CH_CFG_USE_MUTEXES || defined(__DOXYGEN__)
|
||
|
test_print("--- Mutex : ");
|
||
|
test_printn(sizeof(mutex_t));
|
||
|
test_println(" bytes");
|
||
|
#endif]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The size of a condition variable is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[#if CH_CFG_USE_CONDVARS || defined(__DOXYGEN__)
|
||
|
test_print("--- CondV.: ");
|
||
|
test_printn(sizeof(condition_variable_t));
|
||
|
test_println(" bytes");
|
||
|
#endif]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The size of an event source is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[#if CH_CFG_USE_EVENTS || defined(__DOXYGEN__)
|
||
|
test_print("--- EventS: ");
|
||
|
test_printn(sizeof(event_source_t));
|
||
|
test_println(" bytes");
|
||
|
#endif]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The size of an event listener is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[#if CH_CFG_USE_EVENTS || defined(__DOXYGEN__)
|
||
|
test_print("--- EventL: ");
|
||
|
test_printn(sizeof(event_listener_t));
|
||
|
test_println(" bytes");
|
||
|
#endif]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
<step>
|
||
|
<description>
|
||
|
<value>The size of a mailbox is printed.</value>
|
||
|
</description>
|
||
|
<tags>
|
||
|
<value />
|
||
|
</tags>
|
||
|
<code>
|
||
|
<value><![CDATA[#if CH_CFG_USE_MAILBOXES || defined(__DOXYGEN__)
|
||
|
test_print("--- MailB.: ");
|
||
|
test_printn(sizeof(mailbox_t));
|
||
|
test_println(" bytes");
|
||
|
#endif]]></value>
|
||
|
</code>
|
||
|
</step>
|
||
|
</steps>
|
||
|
</case>
|
||
|
</cases>
|
||
|
</sequence>
|
||
|
</sequences>
|
||
|
</instance>
|
||
|
</instances>
|
||
|
<exportedFeatures />
|
||
|
</application>
|
||
|
</SPC5-Config>
|