Anna Bridge 2016-05-13 14:27:05 +01:00
commit cd4172a80b
10 changed files with 331 additions and 65 deletions

View File

@ -0,0 +1,110 @@
#include "mbed.h"
#include "test_env.h"
#include "unity.h"
#include "utest.h"
#include "rtos.h"
using namespace utest::v1;
// Tasks with different functions to test on threads
void increment(const void *var) {
(*(int *)var)++;
}
void increment_with_yield(const void *var) {
Thread::yield();
(*(int *)var)++;
}
void increment_with_wait(const void *var) {
Thread::wait(100);
(*(int *)var)++;
}
void increment_with_child(const void *var) {
Thread child(increment, (void*)var);
child.join();
}
void increment_with_murder(const void *var) {
Thread child(increment_with_wait, (void*)var);
// Kill child before it can increment var
child.terminate();
(*(int *)var)++;
}
// Tests that spawn tasks in different configurations
template <void (*F)(const void *)>
void test_single_thread() {
int var = 0;
Thread thread(F, &var);
thread.join();
TEST_ASSERT_EQUAL(var, 1);
}
template <int N, void (*F)(const void *)>
void test_parallel_threads() {
int var = 0;
Thread *threads[N];
for (int i = 0; i < N; i++) {
threads[i] = new Thread(F, &var);
}
for (int i = 0; i < N; i++) {
threads[i]->join();
delete threads[i];
}
TEST_ASSERT_EQUAL(var, N);
}
template <int N, void (*F)(const void *)>
void test_serial_threads() {
int var = 0;
for (int i = 0; i < N; i++) {
Thread thread(F, &var);
thread.join();
}
TEST_ASSERT_EQUAL(var, N);
}
status_t test_setup(const size_t number_of_cases) {
GREENTEA_SETUP(40, "default_auto");
return verbose_test_setup_handler(number_of_cases);
}
// Test cases
Case cases[] = {
Case("Testing single thread", test_single_thread<increment>),
Case("Testing parallel threads", test_parallel_threads<3, increment>),
Case("Testing serial threads", test_serial_threads<10, increment>),
Case("Testing single thread with yield", test_single_thread<increment_with_yield>),
Case("Testing parallel threads with yield", test_parallel_threads<3, increment_with_yield>),
Case("Testing serial threads with yield", test_serial_threads<10, increment_with_yield>),
Case("Testing single thread with wait", test_single_thread<increment_with_wait>),
Case("Testing parallel threads with wait", test_parallel_threads<3, increment_with_wait>),
Case("Testing serial threads with wait", test_serial_threads<10, increment_with_wait>),
Case("Testing single thread with child", test_single_thread<increment_with_child>),
Case("Testing parallel threads with child", test_parallel_threads<3, increment_with_child>),
Case("Testing serial threads with child", test_serial_threads<10, increment_with_child>),
Case("Testing single thread with murder", test_single_thread<increment_with_murder>),
Case("Testing parallel threads with murder", test_parallel_threads<3, increment_with_murder>),
Case("Testing serial threads with murder", test_serial_threads<10, increment_with_murder>),
};
Specification specification(test_setup, cases);
int main() {
return !Harness::run(specification);
}

View File

@ -26,34 +26,82 @@
namespace rtos {
Thread::Thread(void (*task)(void const *argument), void *argument,
osPriority priority, uint32_t stack_size, unsigned char *stack_pointer) {
Thread::Thread(osPriority priority,
uint32_t stack_size, unsigned char *stack_pointer):
_tid(NULL), _dynamic_stack(stack_pointer == NULL) {
#ifdef __MBED_CMSIS_RTOS_CM
_thread_def.pthread = task;
_thread_def.tpriority = priority;
_thread_def.stacksize = stack_size;
if (stack_pointer != NULL) {
_thread_def.stack_pointer = (uint32_t*)stack_pointer;
_dynamic_stack = false;
} else {
_thread_def.stack_pointer = new uint32_t[stack_size/sizeof(uint32_t)];
if (_thread_def.stack_pointer == NULL)
_thread_def.stack_pointer = (uint32_t*)stack_pointer;
#endif
}
Thread::Thread(void (*task)(void const *argument), void *argument,
osPriority priority, uint32_t stack_size, unsigned char *stack_pointer):
_tid(NULL), _dynamic_stack(stack_pointer == NULL) {
#ifdef __MBED_CMSIS_RTOS_CM
_thread_def.tpriority = priority;
_thread_def.stacksize = stack_size;
_thread_def.stack_pointer = (uint32_t*)stack_pointer;
#endif
switch(start(task, argument)) {
case osErrorResource:
error("OS ran out of threads!\n");
break;
case osErrorParameter:
error("Thread already running!\n");
break;
case osErrorNoMemory:
error("Error allocating the stack memory\n");
_dynamic_stack = true;
default:
break;
}
}
osStatus Thread::start(void (*task)(void const *argument), void *argument) {
if (_tid != NULL) {
return osErrorParameter;
}
#ifdef __MBED_CMSIS_RTOS_CM
_thread_def.pthread = task;
if (_thread_def.stack_pointer == NULL) {
_thread_def.stack_pointer = new uint32_t[_thread_def.stacksize/sizeof(uint32_t)];
if (_thread_def.stack_pointer == NULL)
return osErrorNoMemory;
}
//Fill the stack with a magic word for maximum usage checking
for (uint32_t i = 0; i < (stack_size / sizeof(uint32_t)); i++) {
for (uint32_t i = 0; i < (_thread_def.stacksize / sizeof(uint32_t)); i++) {
_thread_def.stack_pointer[i] = 0xE25A2EA5;
}
#endif
_tid = osThreadCreate(&_thread_def, argument);
if (_tid == NULL) {
if (_dynamic_stack) delete[] (_thread_def.stack_pointer);
return osErrorResource;
}
return osOK;
}
osStatus Thread::terminate() {
return osThreadTerminate(_tid);
}
osStatus Thread::join() {
while (true) {
uint8_t state = get_state();
if (state == Thread::Inactive || state == osErrorParameter) {
return osOK;
}
osStatus status = yield();
if (status != osOK) {
return status;
}
}
}
osStatus Thread::set_priority(osPriority priority) {
return osThreadSetPriority(_tid, priority);
}

View File

@ -30,6 +30,15 @@ namespace rtos {
/** The Thread class allow defining, creating, and controlling thread functions in the system. */
class Thread {
public:
/** Allocate a new thread without starting execution
@param priority initial priority of the thread function. (default: osPriorityNormal).
@param stack_size stack size (in bytes) requirements for the thread function. (default: DEFAULT_STACK_SIZE).
@param stack_pointer pointer to the stack area to be used by this thread (default: NULL).
*/
Thread(osPriority priority=osPriorityNormal,
uint32_t stack_size=DEFAULT_STACK_SIZE,
unsigned char *stack_pointer=NULL);
/** Create a new thread, and start it executing the specified function.
@param task function to be executed by this thread.
@param argument pointer that is passed to the thread function as start argument. (default: NULL).
@ -42,6 +51,19 @@ public:
uint32_t stack_size=DEFAULT_STACK_SIZE,
unsigned char *stack_pointer=NULL);
/** Starts a thread executing the specified function.
@param task function to be executed by this thread.
@param argument pointer that is passed to the thread function as start argument. (default: NULL).
@return status code that indicates the execution status of the function.
*/
osStatus start(void (*task)(void const *argument), void *argument=NULL);
/** Wait for thread to terminate
@return status code that indicates the execution status of the function.
@note not callable from interrupt
*/
osStatus join();
/** Terminate execution of a thread and remove it from Active Threads
@return status code that indicates the execution status of the function.
*/
@ -113,17 +135,20 @@ public:
@param signals wait until all specified signal flags set or 0 for any single signal flag.
@param millisec timeout value or 0 in case of no time-out. (default: osWaitForever).
@return event flag information or error code.
@note not callable from interrupt
*/
static osEvent signal_wait(int32_t signals, uint32_t millisec=osWaitForever);
/** Wait for a specified time period in millisec:
@param millisec time delay value
@return status code that indicates the execution status of the function.
@note not callable from interrupt
*/
static osStatus wait(uint32_t millisec);
/** Pass control to next thread that is in state READY.
@return status code that indicates the execution status of the function.
@note not callable from interrupt
*/
static osStatus yield();

View File

@ -27,7 +27,7 @@ sys.path.insert(0, ROOT)
from tools.test_api import test_path_to_name, find_tests, print_tests, build_tests, test_spec_from_test_build
from tools.options import get_default_options_parser
from tools.build_api import build_project
from tools.build_api import build_project, build_library
from tools.targets import TARGET_MAP
from tools.utils import mkdir
@ -115,10 +115,17 @@ if __name__ == '__main__':
if not base_source_paths:
base_source_paths = ['.']
target = TARGET_MAP[options.mcu]
lib_build_res = build_library(base_source_paths, options.build_dir, target, options.tool,
options=options.options,
jobs=options.jobs,
clean=options.clean,
archive=False)
# Build all the tests
test_build = build_tests(tests, base_source_paths, options.build_dir, target, options.tool,
test_build = build_tests(tests, [options.build_dir], options.build_dir, target, options.tool,
options=options.options,
clean=options.clean,
jobs=options.jobs)

View File

@ -2041,13 +2041,13 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name,
}
for test_name, test_path in tests.iteritems():
test_build_path = os.path.join(build_path, test_path)
src_path = base_source_paths + [test_path]
artifact_name = os.path.join(test_path, test_name)
bin_file = build_project(src_path, build_path, target, toolchain_name,
bin_file = build_project(src_path, test_build_path, target, toolchain_name,
options=options,
jobs=jobs,
clean=clean,
name=artifact_name,
name=test_name,
report=report,
properties=properties,
verbose=verbose)

View File

@ -29,6 +29,7 @@ from multiprocessing import Pool, cpu_count
from tools.utils import run_cmd, mkdir, rel_path, ToolException, NotSupportedException, split_path
from tools.settings import BUILD_OPTIONS, MBED_ORG_USER
import tools.hooks as hooks
from hashlib import md5
#Disables multiprocessing if set to higher number than the host machine CPUs
@ -210,6 +211,7 @@ class mbedToolchain:
self.has_config = False
self.build_all = False
self.build_dir = None
self.timestamp = time()
self.jobs = 1
@ -476,19 +478,35 @@ class mbedToolchain:
mkdir(obj_dir)
return join(obj_dir, name + '.o')
def get_inc_file(self, includes):
include_file = join(self.build_dir, ".includes_%s.txt" % self.inc_md5)
if not exists(include_file):
with open(include_file, "wb") as f:
cmd_list = []
for c in includes:
if c:
cmd_list.append(('-I%s' % c).replace("\\", "/"))
string = " ".join(cmd_list)
f.write(string)
return include_file
def compile_sources(self, resources, build_path, inc_dirs=None):
# Web IDE progress bar for project build
files_to_compile = resources.s_sources + resources.c_sources + resources.cpp_sources
self.to_be_compiled = len(files_to_compile)
self.compiled = 0
#for i in self.build_params:
# self.debug(i)
# self.debug("%s" % self.build_params[i])
inc_paths = resources.inc_dirs
if inc_dirs is not None:
inc_paths.extend(inc_dirs)
# De-duplicate include paths
inc_paths = set(inc_paths)
# Sort include paths for consistency
inc_paths = sorted(set(inc_paths))
# Unique id of all include paths
self.inc_md5 = md5(' '.join(inc_paths)).hexdigest()
# Where to store response files
self.build_dir = build_path
objects = []
queue = []
@ -496,6 +514,7 @@ class mbedToolchain:
# The dependency checking for C/C++ is delegated to the compiler
base_path = resources.base_path
# Sort compile queue for consistency
files_to_compile.sort()
work_dir = getcwd()
@ -641,28 +660,6 @@ class mbedToolchain:
else:
raise ToolException(_stderr)
def compile(self, cc, source, object, includes):
_, ext = splitext(source)
ext = ext.lower()
command = cc + ['-D%s' % s for s in self.get_symbols()] + ["-I%s" % i for i in includes] + ["-o", object, source]
if hasattr(self, "get_dep_opt"):
base, _ = splitext(object)
dep_path = base + '.d'
command.extend(self.get_dep_opt(dep_path))
if hasattr(self, "cc_extra"):
command.extend(self.cc_extra(base))
return [command]
def compile_c(self, source, object, includes):
return self.compile(self.cc, source, object, includes)
def compile_cpp(self, source, object, includes):
return self.compile(self.cppc, source, object, includes)
def build_library(self, objects, dir, name):
needed_update = False
lib = self.STD_LIB_NAME % name
@ -712,12 +709,12 @@ class mbedToolchain:
return bin, needed_update
def default_cmd(self, command):
self.debug("Command: %s"% ' '.join(command))
_stdout, _stderr, _rc = run_cmd(command)
# Print all warning / erros from stderr to console output
for error_line in _stderr.splitlines():
print error_line
self.debug("Command: %s"% ' '.join(command))
self.debug("Return: %s"% _rc)
for output_line in _stdout.splitlines():

View File

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
"""
import re
from os.path import join, dirname, basename
from os.path import join, dirname, splitext, basename, exists
from tools.toolchains import mbedToolchain
from tools.settings import ARM_BIN, ARM_INC, ARM_LIB, MY_ARM_CLIB, ARM_CPPLIB, GOANNA_PATH
@ -78,11 +78,6 @@ class ARM(mbedToolchain):
self.ar = join(ARM_BIN, "armar")
self.elf2bin = join(ARM_BIN, "fromelf")
def remove_option(self, option):
for tool in [self.asm, self.cc, self.cppc]:
if option in tool:
tool.remove(option)
def parse_dependencies(self, dep_path):
dependencies = []
for line in open(dep_path).readlines():
@ -112,9 +107,14 @@ class ARM(mbedToolchain):
match.group('message')
)
def get_dep_opt(self, dep_path):
def get_dep_option(self, object):
base, _ = splitext(object)
dep_path = base + '.d'
return ["--depend", dep_path]
def get_compile_options(self, defines, includes):
return ['-D%s' % d for d in defines] + ['--via', self.get_inc_file(includes)]
@hook_tool
def assemble(self, source, object, includes):
# Preprocess first, then assemble
@ -123,8 +123,8 @@ class ARM(mbedToolchain):
tempfile = join(dir, basename(object) + '.E.s')
# Build preprocess assemble command
cmd_pre = self.asm + ['-D%s' % s for s in self.get_symbols() + self.macros] + ["-I%s" % i for i in includes] + ["-E", "-o", tempfile, source]
cmd_pre = self.asm + self.get_compile_options(self.get_symbols(), includes) + ["-E", "-o", tempfile, source]
# Build main assemble command
cmd = self.asm + ["-o", object, tempfile]
@ -135,6 +135,25 @@ class ARM(mbedToolchain):
# Return command array, don't execute
return [cmd_pre, cmd]
@hook_tool
def compile(self, cc, source, object, includes):
# Build compile command
cmd = cc + self.get_compile_options(self.get_symbols(), includes)
cmd.extend(self.get_dep_option(object))
cmd.extend(["-o", object, source])
# Call cmdline hook
cmd = self.hook.get_cmdline_compiler(cmd)
return [cmd]
def compile_c(self, source, object, includes):
return self.compile(self.cc, source, object, includes)
def compile_cpp(self, source, object, includes):
return self.compile(self.cppc, source, object, includes)
@hook_tool
def link(self, output, objects, libraries, lib_dirs, mem_map):

View File

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
"""
import re
from os.path import join, basename, splitext, dirname
from os.path import join, basename, splitext, dirname, exists
from tools.toolchains import mbedToolchain
from tools.settings import GCC_ARM_PATH, GCC_CR_PATH
@ -68,7 +68,7 @@ class GCC(mbedToolchain):
"-Wno-unused-parameter", "-Wno-missing-field-initializers",
"-fmessage-length=0", "-fno-exceptions", "-fno-builtin",
"-ffunction-sections", "-fdata-sections",
"-MMD", "-fno-delete-null-pointer-checks", "-fomit-frame-pointer"
"-fno-delete-null-pointer-checks", "-fomit-frame-pointer"
] + self.cpu
if "save-asm" in self.options:
@ -161,10 +161,18 @@ class GCC(mbedToolchain):
message + match.group('message')
)
def get_dep_option(self, object):
base, _ = splitext(object)
dep_path = base + '.d'
return ["-MD", "-MF", dep_path]
def get_compile_options(self, defines, includes):
return ['-D%s' % d for d in defines] + ['@%s' % self.get_inc_file(includes)]
@hook_tool
def assemble(self, source, object, includes):
# Build assemble command
cmd = self.asm + ['-D%s' % s for s in self.get_symbols() + self.macros] + ["-I%s" % i for i in includes] + ["-o", object, source]
cmd = self.asm + self.get_compile_options(self.get_symbols(), includes) + ["-o", object, source]
# Call cmdline hook
cmd = self.hook.get_cmdline_assembler(cmd)
@ -172,6 +180,26 @@ class GCC(mbedToolchain):
# Return command array, don't execute
return [cmd]
@hook_tool
def compile(self, cc, source, object, includes):
# Build compile command
cmd = cc + self.get_compile_options(self.get_symbols(), includes)
cmd.extend(self.get_dep_option(object))
cmd.extend(["-o", object, source])
# Call cmdline hook
cmd = self.hook.get_cmdline_compiler(cmd)
return [cmd]
def compile_c(self, source, object, includes):
return self.compile(self.cc, source, object, includes)
def compile_cpp(self, source, object, includes):
return self.compile(self.cppc, source, object, includes)
@hook_tool
def link(self, output, objects, libraries, lib_dirs, mem_map):
libs = []

View File

@ -16,7 +16,7 @@ limitations under the License.
"""
import re
from os import remove
from os.path import join, exists, dirname
from os.path import join, exists, dirname, splitext, exists
from tools.toolchains import mbedToolchain
from tools.settings import IAR_PATH
@ -72,6 +72,10 @@ class IAR(mbedToolchain):
self.ar = join(IAR_BIN, "iarchive")
self.elf2bin = join(IAR_BIN, "ielftool")
def parse_dependencies(self, dep_path):
return [path.strip() for path in open(dep_path).readlines()
if (path and not path.isspace())]
def parse_output(self, output):
for line in output.splitlines():
match = IAR.DIAGNOSTIC_PATTERN.match(line)
@ -93,20 +97,22 @@ class IAR(mbedToolchain):
match.group('message')
)
def get_dep_opt(self, dep_path):
def get_dep_option(self, object):
base, _ = splitext(object)
dep_path = base + '.d'
return ["--dependencies", dep_path]
def cc_extra(self, base):
def cc_extra(self, object):
base, _ = splitext(object)
return ["-l", base + '.s']
def parse_dependencies(self, dep_path):
return [path.strip() for path in open(dep_path).readlines()
if (path and not path.isspace())]
def get_compile_options(self, defines, includes):
return ['-D%s' % d for d in defines] + ['-f', self.get_inc_file(includes)]
@hook_tool
def assemble(self, source, object, includes):
# Build assemble command
cmd = self.asm + ['-D%s' % s for s in self.get_symbols() + self.macros] + ["-I%s" % i for i in includes] + ["-o", object, source]
cmd = self.asm + self.get_compile_options(self.get_symbols(), includes) + ["-o", object, source]
# Call cmdline hook
cmd = self.hook.get_cmdline_assembler(cmd)
@ -114,6 +120,28 @@ class IAR(mbedToolchain):
# Return command array, don't execute
return [cmd]
@hook_tool
def compile(self, cc, source, object, includes):
# Build compile command
cmd = cc + self.get_compile_options(self.get_symbols(), includes)
cmd.extend(self.get_dep_option(object))
cmd.extend(self.cc_extra(object))
cmd.extend(["-o", object, source])
# Call cmdline hook
cmd = self.hook.get_cmdline_compiler(cmd)
return [cmd]
def compile_c(self, source, object, includes):
return self.compile(self.cc, source, object, includes)
def compile_cpp(self, source, object, includes):
return self.compile(self.cppc, source, object, includes)
@hook_tool
def link(self, output, objects, libraries, lib_dirs, mem_map):
# Build linker command

View File

@ -34,8 +34,12 @@ def cmd(l, check=True, verbose=False, shell=False, cwd=None):
def run_cmd(command, wd=None, redirect=False):
assert is_cmd_valid(command[0])
p = Popen(command, stdout=PIPE, stderr=STDOUT if redirect else PIPE, cwd=wd)
_stdout, _stderr = p.communicate()
try:
p = Popen(command, stdout=PIPE, stderr=STDOUT if redirect else PIPE, cwd=wd)
_stdout, _stderr = p.communicate()
except:
print "[OS ERROR] Command: "+(' '.join(command))
raise
return _stdout, _stderr, p.returncode