00001 /** \file 00002 * @brief MSP430/GCC port of the RTOS 00003 * 00004 * @page msp430gcc_port HAL for MSP430 family with mspgcc compiler 00005 * 00006 * This is the TiROS hardware abstraction layer for the Texas 00007 * Instruments MSP430x family of embedded microcontrollers. This 00008 * microcontroller family can support software interrupts. These 00009 * microcontrollers can be driven from a 32kHz crystal oscillator. 00010 * The time functions have been written assuming such an oscillator 00011 * drives the timer. If this is not the case, modify the functions in porttime.h 00012 * and the timer initialization functions in tr_port.c. 00013 * 00014 * Note that in the MSP430 architecture, the low-power state of the 00015 * processor is stored in the status register. Since this is part of 00016 * the context, per-task power control is automatic. To keep the 00017 * processor in low-power state during idle time, simple define 00018 * the idle task as 00019 * \code 00020 * void idle_task(void *dummy) 00021 * { 00022 * while(1) { 00023 * _BIS_SR(LPM3_bits); 00024 * } 00025 * } 00026 * \endcode 00027 * 00028 * Tested with TI MSP430x1611 00029 * 00030 * <H2> Configuration Options: </H2> 00031 * <DL> 00032 * <DT> TRPORT_MSP_OSTIMER [A | B] : <DD>Options are A or B. The MSP430 family 00033 * supports two timers, TimerA and TimerB. TimerA is available on the 00034 * entire family line. TimerB is only available on some. By default, 00035 * if this configuration option is not set, TimerA is used as source 00036 * for the timer interrupt. 00037 * <DT> TIROS_KERNEL_TRAP_ENABLED [0|1]: <DD> If this define is set to 1, OS 00038 * kernel context switches from ISRs are made using a software trap 00039 * (i.e.) by causing an interrupt under software control. This makes 00040 * ISRs very efficient. The MSP430/GCC port supports kernel traps. So 00041 * this is enabled as a default. To disable it, set 00042 * TIROS_KERNEL_TRAP_ENABLED to 0 in proj_config.h. 00043 * <DT> USER_DEFINED_KERNEL_TRAP : <DD> If TIROS_KERNEL_TRAP_ENABLED is set 00044 * to 1, kernel traps are enabled. The default implementation uses 00045 * Port 2, pins. By setting a specific pin, an interrupt is forced. 00046 * The user can override this default and specify a different 00047 * interrupt handler for this purpose. In this case, create user 00048 * definitions for isr_kernel_trap and OS_KERNEL_TRAP, and 00049 * hal_setup_kernel_trap() . Do not define or set these functions if 00050 * TIROS_KERNEL_TRAP_ENABLED is set to 0. 00051 * </DL> 00052 * <H2> Writing ISRs. </H2> 00053 * 00054 * If no TiROS API functions will be called, interrupts will stay 00055 * disabled, then the ISR can be 00056 * written very simply. See the requirements in \ref isr. 00057 * \code 00058 * interrupt (IVECTOR) ISR_function(void) 00059 * { 00060 * // do stuff 00061 * } 00062 * \endcode 00063 * 00064 * If TiROS API calls have to be used, 00065 * the form of the ISR depends on whether TIROS_KERNEL_TRAP_ENABLED is set 00066 * (It is set as a default). If TIROS_KERNEL_TRAP_ENABLED is set to 1, the 00067 * ISR is simple 00068 * \code 00069 * interrupt (IVECTOR) ISR_function(void) 00070 * { 00071 * int x; 00072 * OS_CRITICAL_ENABLE(); // Only needed if any critical sections 00073 * // will be used and interrupt nesting is enabled. 00074 * 00075 * OS_ISR_BEGIN(); // Mark the beginning of an ISR. Note 00076 * // that if OS_CRITICAL_ENABLE() is also 00077 * // present, then this comes after 00078 * // that. 00079 * 00080 * x = 3; 00081 * Do more stuff .... 00082 * 00083 * OS_ISR_END(); 00084 * } 00085 * \endcode 00086 * 00087 * 00088 * If TIROS_KERNEL_TRAP_ENABLED is set to 0, the ISR would look as below. 00089 * Use the ISR implementation in tr_port.c as a reference. 00090 * \code 00091 * interrupt (IVECTOR) ISR_function(void) __attribute__ ( (naked)) 00092 * interrupt (IVECTOR) ISR_function(void) 00093 * { 00094 * HALINT_ISR_ENTRY(); 00095 * OS_ISR_BEGIN(); 00096 * perform_actions(); 00097 * OS_ISR_END(); 00098 * HALINT_ISR_EXIT(); 00099 * } 00100 * \endcode 00101 * 00102 * 00103 */ 00104 00105 /* Author: Ratish J. Punnoose, 2006 00106 * This file is part of TiROS, the Tickless Real-Time Operating System. 00107 * Copyright(c) 2006, 2007: Ratish J. Punnoose. 00108 * Copyright(c) 2006 Sandia Corporation. Under the terms of Contract 00109 * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains 00110 * certain rights in this software. 00111 * 00112 * TiROS is free software; you can redistribute it and/or modify it under 00113 * the terms of the GNU General Public License as published by the Free 00114 * Software Foundation; either version 2 or (at your option) any later version. 00115 * 00116 * TiROS is distributed in the hope that it will be useful, but WITHOUT ANY 00117 * WARRANTY; without even the implied warranty of MERCHANTABILITY or 00118 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 00119 * for more details. 00120 * 00121 * You should have received a copy of the GNU General Public License along 00122 * with TiROS; if not, write to the Free Software Foundation, Inc., 00123 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 00124 * 00125 * As a special exception, if other files instantiate templates or use macros 00126 * or inline functions from this file, or you compile this file and link it 00127 * with other works to produce a work based on this file, this file does not 00128 * by itself cause the resulting work to be covered by the GNU General Public 00129 * License. However the source code for this file must still be made available 00130 * in accordance with section (3) of the GNU General Public License. 00131 * 00132 * This exception does not invalidate any other reasons why a work based on 00133 * this file might be covered by the GNU General Public License. 00134 * 00135 */ 00136 00137 00138 #ifndef __MSP430GCC_TIROS_PORT_H 00139 #define __MSP430GCC_TIROS_PORT_H 00140 /*@ignore@*/ 00141 #include "proj_config.h" 00142 /*@end@*/ 00143 00144 #include <io.h> 00145 #include <signal.h> 00146 #include <stdint.h> 00147 00148 /* The data type used by this port for subtime_t */ 00149 #define subtime_t uint16_t 00150 #define LT_INLINE static inline 00151 00152 00153 00154 #include <tiros/tr_time.h> 00155 /* Insert your port for time functions for your hardware */ 00156 #include "porttime.h" 00157 00158 /* Define the value of infinite time for this port */ 00159 #define TRPORT_INF_TIME {~0 , ~0 } 00160 00161 00162 00163 /* The msp port can use registers for passing return values to 00164 blocked tasks */ 00165 #define TIROS_REGISTER_PASSING 00166 00167 00168 00169 /* Define basic types for the hardware word, boolean, and pointer */ 00170 typedef uintptr_t osword_t; 00171 typedef void* osptr_t; 00172 typedef osword_t* osstkptr_t; 00173 typedef void (* osfnptr_t)(void *); 00174 typedef uintptr_t osptrword_t; 00175 00176 #define ILLEGAL_STACK ((osstkptr_t) ~0) 00177 #define ILLEGAL_ADDR ((osptr_t) ~0) 00178 00179 00180 00181 /* Port specific function implemented in tr_port.c */ 00182 00183 /* \name Context_switching_functions 00184 * @{ */ 00185 00186 /* Initialize the stack for a task. 00187 * @param stk Pointer to the stack. 00188 * @param stacksize Size of the stack. 00189 * @param PC Program counter. 00190 * @param init_arg Initial argument passed to the task */ 00191 /*@shared@*/ 00192 osstkptr_t hal_stk_init(osword_t *stk, osword_t stacksize, osfnptr_t pc, 00193 osptr_t init_arg); 00194 00195 00196 #ifdef TIROS_REGISTER_PASSING 00197 /* Set a return value for a task whose context is stored. 00198 * 00199 * This injects a return value into the context of a task that is currently frozen. 00200 * @param stackptr Pointer to the stack. The stack should also contain the context. 00201 * @param retval The return value to be injected into the task context */ 00202 static inline void hal_retval_set(osstkptr_t stackptr, 00203 /*@null@*/ osptr_t retval) 00204 { 00205 /* GCC passes the return value in register 15, which is what 00206 * stackptr points to */ 00207 *stackptr = (osword_t)retval; 00208 } 00209 #endif 00210 00211 00212 00213 void hal_init(void); 00214 00215 00216 void hal_time_get(/*@out@*/ trtime_t *t); 00217 int hal_time_set(const trtime_t *t); 00218 00219 00220 00221 /* Set an alarm. 00222 * When the alarm is reached, osint_alarm_reached is called 00223 * @param lt Pointer to time */ 00224 void hal_alarm_set(/*@null@*/ const trtime_t *lt); 00225 00226 00227 00228 00229 00230 00231 /* Enable critical section calls in the the called function 00232 * 00233 * This has to be called at the beginning of the function before 00234 * calling OS_CRITICAL_BEGIN or OS_CRITICAL_END. This should be the 00235 * first call after variable declaration. These section should not 00236 * be added within an ISR if Interrupt nesting is turned on, because 00237 * the interrupt nesting adds this in automatically, and duplication 00238 * will result in compiler complaints. */ 00239 #define OS_PORT_CRITICAL_ENABLE() /*@unused@*/ osword_t osint_int_status 00240 #define OS_PORT_ISR_BEGIN() \ 00241 extern uint8_t os_isr_nesting; \ 00242 os_isr_nesting++; 00243 00244 00245 00246 00247 #ifndef S_SPLINT_S 00248 /* Begin a critical section 00249 * 00250 * The current interrupt state is saved in osint_int_status and then 00251 * interrupts are disabled */ 00252 #define OS_PORT_CRITICAL_BEGIN() \ 00253 asm volatile("mov r2, %0 \n\t" \ 00254 "dint \n\t" : "=r" (osint_int_status) : ); \ 00255 osint_int_status &= GIE; 00256 00257 00258 /* End a critical section 00259 * 00260 * The saved interrupt state is restored */ 00261 #define OS_PORT_CRITICAL_END() \ 00262 asm volatile("bis %0, r2 \n\t" : : "r" (osint_int_status) ); 00263 00264 /* Enable interrupts 00265 * This allows the user to explicitly enable interrupts */ 00266 #define OS_PORT_INT_ENABLE() _EINT() 00267 00268 /* Disable interrupts 00269 * This allows the user to explicitly disable interrupts */ 00270 #define OS_PORT_INT_DISABLE() _DINT() 00271 00272 00273 00274 00275 /* Context switch from user level (non-ISR) 00276 * This function is called from within an OS call, to switch the 00277 * running process. It also provides a return value. This is a 00278 * pseudo return-value. This function does not actually determine 00279 * the value returned. The return value is set by hal_retval_set 00280 * which is often invoked when waking the process up from a blocked 00281 * state. 00282 * In this mspgcc port, the hal_ctxt_switch has the naked attribute to 00283 * prevent gcc from adding miscellaneous header code to the 00284 * function. This happens when compiling without optimization. 00285 * 00286 * @return Return value. */ 00287 osstkptr_t hal_ctxt_switch(void) __attribute__ ( (naked) ) ; 00288 00289 /* Initially 14 words are used to store MSP430 register intialization, 00290 * R0 - R15 , without R1, R3*/ 00291 #define MSP430_INIT_STACK_OCCUPANCY 14 00292 00293 #define TRPORT_MIN_CTXT_SZ (MSP430_INIT_STACK_OCCUPANCY + 1 ) 00294 00295 00296 /* Load the given context 00297 * @param ctxt_ptr Pointer to task context */ 00298 static inline void hal_ctxt_load(osstkptr_t ctxt_ptr) 00299 { 00300 /* In this port, the stack pointer provides the contest */ 00301 asm volatile ( 00302 "mov.w %0, r1 \n\t" 00303 "pop r15 \n\t" 00304 "pop r14 \n\t" 00305 "pop r13 \n\t" 00306 "pop r12 \n\t" 00307 "pop r11 \n\t" 00308 "pop r10 \n\t" 00309 "pop r9 \n\t" 00310 "pop r8 \n\t" 00311 "pop r7 \n\t" 00312 "pop r6 \n\t" 00313 "pop r5 \n\t" 00314 "pop r4 \n\t" 00315 "reti \n\t" 00316 : /* no output registers */ 00317 : "r" (ctxt_ptr) 00318 ); 00319 } 00320 00321 00322 00323 /* HAL Internal macro used to save the registers. 00324 * This can be called from an interrupt function. It does not save the 00325 * status register. If being called from non-interrupt code, the 00326 * status register should be saved before calling this function */ 00327 #define HALINT_CTXT_SAVE() \ 00328 "push r4 \n\t" \ 00329 "push r5 \n\t" \ 00330 "push r6 \n\t" \ 00331 "push r7 \n\t" \ 00332 "push r8 \n\t" \ 00333 "push r9 \n\t" \ 00334 "push r10 \n\t" \ 00335 "push r11 \n\t" \ 00336 "push r12 \n\t" \ 00337 "push r13 \n\t" \ 00338 "push r14 \n\t" \ 00339 "push r15 \n\t" \ 00340 "mov.w r1, r15 \n\t" 00341 00342 00343 00344 #endif /* Bypass SPLINT verification */ 00345 00346 /* @} End context switching functions */ 00347 00348 00349 00350 00351 00352 00353 00354 #ifndef TRPORT_RESPONSE_TIME 00355 #define PORT_WAKEUP_RESPONSE_SUBUNITS 40 00356 #define TRPORT_RESPONSE_TIME {0, PORT_WAKEUP_RESPONSE_SUBUNITS } 00357 #endif 00358 00359 00360 00361 00362 00363 00364 #ifndef TRPORT_MSP_OSTIMER 00365 /* Which timer on the MSP430 should be used for servicing the ticks. 00366 * For a default use TIMER A since it is available on all the MSP430x 00367 * family. */ 00368 #define TRPORT_MSP_OSTIMER A 00369 #endif 00370 00371 00372 #ifndef TIROS_KERNEL_TRAP_ENABLED 00373 /* This port can support kernel traps and is thus enabled as a 00374 * default. The user can override this by setting 00375 * \code 00376 * #define TIROS_KERNEL_TRAP_ENABLED 0 00377 * \endcode 00378 * in the proj_config.h file */ 00379 #define TIROS_KERNEL_TRAP_ENABLED 1 00380 #endif 00381 00382 00383 00384 00385 00386 00387 00388 00389 #if (TIROS_KERNEL_TRAP_ENABLED == 1) 00390 /* -------------------------------------------------- */ 00391 00392 00393 00394 00395 #ifndef USER_DEFINED_KERNEL_TRAP 00396 00397 /* Function to setup a kernel trap. If a user defined kernel trap is 00398 * used, then define USER_DEFINED_KERNEL_TRAP and implement 00399 * hal_setup_kernel_trap within user code. The declaration can be 00400 * placed in proj_config.h . */ 00401 void hal_setup_kernel_trap(void); 00402 00403 /* We will use one of the pins on port 1 as our kernel trap. Feel 00404 * free to override this if you want to use a different kernel trap. 00405 * We will use P2.5. See section 9-5 of the MSP430 family 00406 * documentation for use of this as a software interrupt. 00407 */ 00408 #define KTRAP_PIN BIT5 00409 /* Force a software interrupt */ 00410 static inline void OS_KERNEL_TRAP(void) { P2IFG |= KTRAP_PIN; } 00411 #endif 00412 00413 00414 00415 00416 #else /* TIROS_KERNEL_TRAP_ENABLED == 0 -------------------- */ 00417 /* Undefine USER_DEFINED_KERNEL_TRAP */ 00418 #ifdef USER_DEFINED_KERNEL_TRAP 00419 #undef USER_DEFINED_KERNEL_TRAP 00420 #endif 00421 00422 extern int halint_kernel_trap_flagged; 00423 00424 00425 /* Force a software interrupt */ 00426 static inline void OS_KERNEL_TRAP(void) 00427 { halint_kernel_trap_flagged = 1;} 00428 00429 00430 00431 /* -------------------------------------------------- */ 00432 #endif /* End of TIROS_KERNEL_TRAP_ENABLED */ 00433 00434 00435 00436 00437 #ifdef TIROSINT_DATA_DEBUG 00438 /* Compute the program counter, from the stored context (stack 00439 * pointer) 00440 * This function is used for debugging. Implement this by extracting 00441 * the program counter out of the stack pointer if possible. 00442 * 00443 * @param ctxt_ptr The context of the current task 00444 * @return The program counter */ 00445 osptrword_t hal_pc_from_ctxt(osstkptr_t ctxt_ptr); 00446 00447 00448 00449 00450 #ifdef TIROS_STK_CHECK 00451 /* Return the maximum stack occupancy of the task. 00452 * This hal implemented function should provide the 00453 * maximum stack occupancy of the task whose context is given. 00454 * This is used for debugging. It is valid for this function to 00455 * return 0, if this information is not readily extractable. 00456 * @param ctxt_ptr Context of the task. 00457 * @param base_stkptr The initialization stack value for this task 00458 * that was passed to hal_stk_init 00459 * @param stksize The initial stack size for this task that was 00460 * passed to hal_stk_init 00461 * @return The maximum stack occupancy */ 00462 osword_t hal_stkusage_max(osstkptr_t ctxt_ptr, osstkptr_t base_stkptr, 00463 osword_t stksize); 00464 00465 /* Return the current stack occupancy of the task. 00466 * This hal implemented function should provide the 00467 * current stack occupancy of the task whose context is given. 00468 * This is used for debugging. It is valid for this function to 00469 * return 0, if this information is not readily extractable. 00470 * @param ctxt_ptr Context of the task. 00471 * @param base_stkptr The initialization stack value for this task 00472 * that was passed to hal_stk_init 00473 * @param stksize The initial stack size for this task that was 00474 * passed to hal_stk_init 00475 * @return The current stack occupancy */ 00476 osword_t hal_stkusage_curr(osstkptr_t ctxt_ptr, osstkptr_t base_stkptr, 00477 osword_t stksize); 00478 #endif /*TIROS_STK_CHECK */ 00479 00480 #endif 00481 00482 00483 00484 00485 00486 00487 /* End of tr_port.h ======================================== */ 00488 #endif