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