From e9274448e9058df5a32e43212ee9858006fe1c4d Mon Sep 17 00:00:00 2001 From: gdisirio Date: Tue, 24 Feb 2009 16:07:42 +0000 Subject: [PATCH] git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@802 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- docs/src/articles.dox | 1 + docs/src/stacks.dox | 106 ++++++++++++++++++++++++++++++++++++++++++ io_channels.txt | 85 +++++++++++++++++++++++++++++++++ ports/AVR/chcore.h | 10 ++-- src/chthreads.c | 13 ++++-- src/include/debug.h | 17 +++++-- todo.txt | 11 +++-- 7 files changed, 227 insertions(+), 16 deletions(-) create mode 100644 docs/src/stacks.dox create mode 100644 io_channels.txt diff --git a/docs/src/articles.dox b/docs/src/articles.dox index 590efdda7..a0426f41c 100644 --- a/docs/src/articles.dox +++ b/docs/src/articles.dox @@ -21,6 +21,7 @@ * @page articles Articles * @{ * ChibiOS/RT Articles and Code Examples: + * - @subpage article_stacks * - @subpage article_mutual_exclusion * - @subpage article_atomic * - @subpage article_saveram diff --git a/docs/src/stacks.dox b/docs/src/stacks.dox new file mode 100644 index 000000000..e824ea04a --- /dev/null +++ b/docs/src/stacks.dox @@ -0,0 +1,106 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @page article_stacks Stacks and stack sizes + * @{ + * In a RTOS like ChibiOS/RT there are several dedicated stacks, each stack + * has a dedicated RAM space that must have a correctly sized assigned area. + *

The stacks

+ * There are several stacks in the systems, some are always present, some + * others are present only in some architectures: + * - Main stack, this stack is used by the @p main() function and the + * thread that execute it. It is not a normal thread stack because it is + * initialized in the startup code and its size is defined in a port + * dependent way. Details are in the various ports documentation. + * - Interrupt Stack, some architectures have a dedicated interrupt + * stack. This is an important feature in a multithreaded environment, + * without a dedicated interrupt stack each thread has to reserve + * enough space, for interrupts servicing, within its own stack. This space, + * multiplied by the total threads number, can be a significant RAM waste. + * - Thread Stack, each thread has a dedicated stack for its own + * execution and context switch. + * - Other Stacks, some architectures (ARM) can have other stacks but + * the OS does not directly use any of them. + * . + *

Risks

+ * The most critical thing when writing an embedded multithreaded application + * is to determine the correct stack size for main, threads and, when present, + * interrupts.
+ * Assign too much space to a stack wastes RAM, assign too little space + * leads to crashes or, worst scenario, hard to track instability. + * + *

Assign the correct size

+ * You may try to examine the asm listings in order to calculate the exact + * stack requirements but this requires much time, experience and patience.
+ * An alternative way is to use an interactive method. Follow this procedure + * for each thread in the system: + * - Enable the following debug options in the kernel: + * - @p CH_DBG_ENABLE_STACK_CHECK, this enables a stack check before any + * context switch. This option halts the system in @p chSysHalt() just + * before a stack overflow happens. + * - @p CH_DBG_FILL_THREADS, this option fills the threads working area + * with an easily recognizable pattern (0x55). + * - Assign a large and safe size to the thread stack, as example 256 bytes + * on 32 MCUs, 128 bytes on 8/16 bit MCUs. This is almost always too much + * for simple threads. + * - Run the application, if the application crashes or halts then increase + * the stack size and repeat (you know how to use the debugger right?). + * - Let the application run and make sure to trigger the thread in a way to + * make it follow most or all its code paths. If the application crashes or + * halts then increase the stack size and repeat. + * - Stop the application using the debugger and examine the thread working + * area (you know what a map file is, right?). You can see that the thread + * stack overwrote the fill pattern (0x55) from the top of the working area + * downward. You can estimate the excess stack by counting the untouched + * locations. + * - Trim down the stack size and repeat until the application still runs + * correctly and you have a decent margin in the stack. + * - Repeat for all the thread classes in the system. + * - Turn off the debug options. + * - Done. + * . + *

Final Notes

+ * Some useful info: + * - Stack overflows are the most common source of problems during development, + * when in trouble with crashes or anomalous behaviors always first verify + * stack sizes. + * - The required stack size can, and very often does change when changing + * compiler vendor, compiler version, compiler options, code type (ARM + * or THUMB as example). + * - Code compiled in THUMB mode uses more stack space compared to the + * same code compiled in ARM mode. In GCC this is related to lack of tail + * calls optimizations in THUMB mode, this is probably true also in other + * compilers. + * - Speed optimized code often requires less stack space compared to space + * optimized code. Be careful when changing optimizations. + * - The interrupts space overhead on the thread stacks (@p INT_REQUIRED_STACK + * defined in @p chcore.h) is included in the total working area size + * by the system macros @p THD_WA_SIZE() and @p WORKING_AREA().
+ * The correct way to reserve space into the thread stacks for interrupts + * processing is to override the @p INT_REQUIRED_STACK default value. + * Architectures with a dedicated interrupt stack do not require changes + * to this value. Resizing of the global interrupt stack may be required + * instead. + * - Often is a good idea to have some extra space in stacks unless you + * are really starved on RAM. Anyway optimize stack space at the very + * end of your development cycle. + * . + */ +/** @} */ diff --git a/io_channels.txt b/io_channels.txt new file mode 100644 index 000000000..ef5f4b6fd --- /dev/null +++ b/io_channels.txt @@ -0,0 +1,85 @@ +I/O Channels + +- Channels are specific for I/O operations, however, a channel can hide a + complex IPC operation. +- Channels are N-sized not necessarily byte-sized. +- Channels support timeout. +- The IOChannel structure hides a virtualized implementation using a VMT. +- The APIs are macros that hide the VMT. +- Channels must support events, at least 3 events are predefined: + 0 - Incoming data event. + 1 - Output queue empty. + 2 - I/O Status Change (at least one status flag was pended). + X - More events can be defined and are channel specific. +- Read/write functions are non blocking and can transfer no data if the + buffers are empty/full. +- Zero sized read and writes simply returns zero, nothing is queued. + +/** + * @brief Returns the channel data unit size. + * @details The channel data unit size is characteristic of the channel and + * cannot be modified. + * @param[in] iop pointer to an IOChannel structure + * @return The channel data unit size in bytes. + */ +size_t chIOGetWidth(const IOChannel *iop); + +/** + * @brief Returns the event sources associated to the channel. + * @details A channel can have associated event sources. The event sources are + * identified by a numerical identifier, the following identifiers + * are predefined: + * - CH_IO_EVT_INPUT signaled when some data is queued in the input buffer. + * - CH_IO_EVT_OUTPUT signaled when the output buffer is emptied. + * - CH_IO_EVT_STATUS signaled when a channel related condition happens. + * + * @param[in] iop pointer to an IOChannel structure + * @param[in] n the numerical identifier. + * @return A pointer to the @p EventSource structure associated to the numerical + * identifier. + * @retval NULL there is no event source associated to the specified + * identifier. + */ +EventSource *chIOGetEventSource(const IOChannel *iop, ioevtsrc_t n); + +/** + * @brief Returns the channel status flags. + * @details The channel status flags are returned and cleared. + * + * @param[in] iop pointer to an IOChannel structure + * @return The status flags. + * @retval 0 no flags pending. + */ +iosts_t chIOGetAndClearStatus(IOChannel *iop); + +/** + * @brief Asynchronous read. + * @details This function reads up to @p n data units into the specified + * buffer without blocking. If there is no data into the input queue + * then the function returns immediatly. + * + * @param[in] iop pointer to an IOChannel structure + * @param[out] buf the buffer where to copy the input data + * @param[in] n the maximum number of data units to transfer + * @return The actual data units number read. + * @retval 0 the input queue is empty, no data transfer was performed. + */ +size_t chIORead(IOChannel *iop, void *buf, size_t n); + +/** + * @brief Asynchronous write. + * @details This function writes up to @p n data units from the specified + * buffer without blocking. If there is no space into the output queue + * then the function returns immediatly. + * + * @param[in] iop pointer to an IOChannel structure + * @param[out] buf the buffer with the data to be written + * @param[in] n the maximum number of data units to transfer + * @return The actual data units number written. + * @retval 0 the output queue is full, no data transfer was performed. + */ +size_t chIOWrite(IOChannel *iop, const void *buf, size_t n); + +bool_t chIOWaitInput(IOChannel *iop, systime_t timeout); + +bool_t chIOWaitOutput(IOChannel *iop, systime_t timeout); diff --git a/ports/AVR/chcore.h b/ports/AVR/chcore.h index 1bb142b55..30eb905fc 100644 --- a/ports/AVR/chcore.h +++ b/ports/AVR/chcore.h @@ -175,16 +175,16 @@ struct context { * This code tricks the compiler to save all the specified registers by * "touching" them. */ -#define PORT_IRQ_PROLOGUE() { \ -asm ("" : : : "r18", "r19", "r20", "r21", "r22", "r23", "r24", \ - "r25", "r26", "r27", "r30", "r31"); \ +#define PORT_IRQ_PROLOGUE() { \ + asm ("" : : : "r18", "r19", "r20", "r21", "r22", "r23", "r24", \ + "r25", "r26", "r27", "r30", "r31"); \ } /** * IRQ epilogue code, inserted at the end of all IRQ handlers enabled to * invoke system APIs. */ -#define PORT_IRQ_EPILOGUE() { \ +#define PORT_IRQ_EPILOGUE() { \ if (chSchRescRequiredI()) \ chSchDoRescheduleI(); \ } @@ -240,7 +240,7 @@ asm ("" : : : "r18", "r19", "r20", "r21", "r22", "r23", "r24", \ * This port function is implemented as inlined code for performance reasons. */ #if ENABLE_WFI_IDLE != 0 -#define port_wait_for_interrupt() { \ +#define port_wait_for_interrupt() { \ asm volatile ("sleep"); \ } #else diff --git a/src/chthreads.c b/src/chthreads.c index d32f9d44a..902867a41 100644 --- a/src/chthreads.c +++ b/src/chthreads.c @@ -59,10 +59,10 @@ Thread *init_thread(Thread *tp, tprio_t prio) { } #if CH_DBG_FILL_THREADS -static void memfill(uint8_t *p, uint32_t n, uint8_t v) { +static void memfill(uint8_t *startp, uint8_t *endp, uint8_t v) { - while (n) - *p++ = v, n--; + while (startp < endp) + *startp++ = v; } #endif @@ -95,7 +95,12 @@ Thread *chThdInit(void *workspace, size_t wsize, (prio <= HIGHPRIO) && (pf != NULL), "chThdInit"); #if CH_DBG_FILL_THREADS - memfill(workspace, wsize, MEM_FILL_PATTERN); + memfill(workspace, + (uint8_t)workspace + sizeof(Thread), + THREAD_FILL_VALUE); + memfill((uint8_t)workspace + sizeof(Thread), + (uint8_t)workspace + wsize + STACK_FILL_VALUE); #endif SETUP_CONTEXT(workspace, wsize, pf, arg); return init_thread(tp, prio); diff --git a/src/include/debug.h b/src/include/debug.h index 2efc6a8c7..82e784040 100644 --- a/src/include/debug.h +++ b/src/include/debug.h @@ -35,10 +35,21 @@ #endif /** - * @brief Fill value for threads working area in debug mode. + * @brief Fill value for thread stack area in debug mode. */ -#ifndef MEM_FILL_PATTERN -#define MEM_FILL_PATTERN 0x55 +#ifndef STACK_FILL_VALUE +#define STACK_FILL_VALUE 0x55 +#endif + +/** + * @brief Fill value for thread area in debug mode. + * @note The chosen default value is 0xFF in order to make evident which + * thread fields were not initialized when inspecting the memory with + * a debugger. A uninitialized field is not an error in itself but it + * better to know it. + */ +#ifndef THREAD_FILL_VALUE +#define THREAD_FILL_VALUE 0xFF #endif /** diff --git a/todo.txt b/todo.txt index aed98175a..4bb9af76d 100644 --- a/todo.txt +++ b/todo.txt @@ -17,11 +17,17 @@ After 1.0.0: * Add checks to all APIs. * Stack checks option. * Threads profiling option. + - Registers clearing on thread start. * Idle loop hook macro. * Switch the configuration options to TRUE/FALSE rather than def/undef. * Remove port_puts() from all the ports. +- Stack sizes article into the documentation. +- Find out and document main stack settings in MSP430 and AVR runtimes. After 1.2.0: +X Abstract I/O channels rather than just serial ports. + - Move the serial drivers implementations in library. Better keep the core + as compact as possible. - Threads Pools manager in the library. - New chThdCreate() that takes just two parameters, a pointer to a thread descriptor and the tread parameter. It could wrap the current variants @@ -30,11 +36,8 @@ After 1.2.0: - OSEK-style simple tasks within the idle thread. ? Think to something for threads restart. ? Multiple heaps, disjoint heaps, heaps in heaps. -- Abstract I/O channels rather than just serial ports. - - Move the serial drivers implementations in library al keep the I/O channel - interface as part of the kernel. Better keep the core as compact as - possible. - Update C++ wrapper (Heap, Pools, Mailboxes and any new feature). +- Think about making threads return void. Ideas for 2.x.x: - Reference counter for threads, concept of detached threads, threads