QDK PIC24/dsPIC-C30 - Quantum Leaps
QDK PIC24/dsPIC-C30 - Quantum Leaps
QDK PIC24/dsPIC-C30 - Quantum Leaps
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
QP state machine frameworks for <strong>PIC24</strong>/<strong>dsPIC</strong><br />
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
Document Revision D<br />
October 2012<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC<br />
www.quantum-leaps.com<br />
www.state-machine.com
Table of Contents<br />
1 Introduction ..................................................................................................................................................... 1<br />
1.1 About QP .................................................................................................................................................... 2<br />
1.2 About the QP Port to <strong>PIC24</strong>/<strong>dsPIC</strong> ............................................................................................................. 3<br />
1.3 What’s Included in the <strong>QDK</strong>-<strong>PIC24</strong>-<strong>dsPIC</strong> ................................................................................................... 4<br />
1.4 Licensing QP .............................................................................................................................................. 4<br />
2 Getting Started ................................................................................................................................................ 5<br />
2.1 Software Installation ....................................................................................................................................... 5<br />
2.2 Building the QP Libraries ............................................................................................................................... 7<br />
2.3 Building and Running the Examples .............................................................................................................. 8<br />
3 Non-Preemptive “Vanilla” Port ...................................................................................................................... 12<br />
3.1 The qep_port.h Header File ........................................................................................................................... 12<br />
3.2 The qf_port.h Header File .............................................................................................................................. 12<br />
3.3 ISRs in the Non-Preemptive “Vanilla” Configuration ...................................................................................... 14<br />
3.4 QP Idle Loop Customization in QF_onIdle() ................................................................................................... 16<br />
4 Preemptive Configuration with QK ................................................................................................................ 17<br />
4.1 QK-specific ISR Entry and Exit Macros (file qk_port.h) .................................................................................. 17<br />
4.2 ISRs with the Preemptive QK Kernel ............................................................................................................. 19<br />
4.3 Idle Loop Customization in the QK Port ......................................................................................................... 22<br />
4.4 Testing QK Preemption Scenarios ................................................................................................................. 23<br />
5 Implementing “Zero Interrupt Latency” with the NMI .................................................................................. 26<br />
5.1 Communication between NMIs and QP ......................................................................................................... 26<br />
5.2 Implementation Considerations for the NMIs ................................................................................................. 26<br />
6 BSP for the Explorer 16 Board ....................................................................................................................... 27<br />
6.1 Setting the Sizes of Stack and Heap .............................................................................................................. 27<br />
6.2 The BSP header file bsp.h ............................................................................................................................. 28<br />
6.3 BSP initialization ............................................................................................................................................ 28<br />
6.4 Starting Interrupts in QF_onStartup() ............................................................................................................. 28<br />
6.5 Assertion Handling Policy in Q_onAssert() .................................................................................................... 28<br />
7 The QS Software Tracing Instrumentation ................................................................................................... 30<br />
7.1 QS Time Stamp Callback QS_onGetTime() ................................................................................................... 31<br />
7.2 QS Trace Output in QF_onIdle()/QK_onIdle() ................................................................................................ 33<br />
7.3 Invoking the QSpy Host Application ............................................................................................................... 33<br />
8 Related Documents and References ............................................................................................................. 34<br />
9 Contact Information ........................................................................................................................................ 35<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
i
1 Introduction<br />
This QP Development Kit (<strong>QDK</strong>) describes how to use the QP state machine frameworks with the<br />
Microchip <strong>PIC24</strong>/<strong>dsPIC</strong> MCUs/DSCs and the Microchip <strong>C30</strong> compiler.<br />
USB cable<br />
to host PC<br />
Figure 1: Explorer 16 board with MPLAB ICD2 debugger<br />
MPLAB ICD2<br />
in-circuit debugger<br />
<strong>PIC24</strong>FJ128GA010<br />
or<br />
<strong>dsPIC</strong>33FJ256GP710<br />
target device<br />
Q-SPY software<br />
tracing output to<br />
host PC (RS232)<br />
Power LED<br />
9V DC power<br />
User LEDs<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
1 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
The actual hardware/software used to test this <strong>QDK</strong> is described below (see Figure 1):<br />
1. Microchip Explorer 16 Development Board with <strong>PIC24</strong>FJ128GA010 or <strong>dsPIC</strong>33FJ256 daughter board<br />
2. Microchip MPLAB® ICD2 in-circuit debugger<br />
3. Microchip MPLAB® IDE v8.83<br />
4. Microchip MPLAB <strong>C30</strong> compiler v3.31<br />
5. QP/C 4.5.02 or higher<br />
As shown in Figure 1, the Explorer 16 Development Board accepts <strong>PIC24</strong> and <strong>dsPIC</strong> daughter boards.<br />
The port has been tested with <strong>PIC24</strong>FJ128GA010 daughter board as well as with <strong>dsPIC</strong>33FJ256<br />
daughter board. However, the described QP port should be applicable to almost all Microchip 16-bit<br />
<strong>PIC24</strong> and <strong>dsPIC</strong> devices.<br />
1.1 About QP<br />
QP is a family of very lightweight, open source, state machine frameworks for<br />
developing event-driven applications. QP enables building well-structured<br />
embedded applications as a set of concurrently executing hierarchical state<br />
machines (UML statecharts) directly in C or C++ without big tools. QP is<br />
described in great detail in the book “Practical UML Statecharts in C/C++,<br />
Second Edition: Event-Driven Programming for Embedded Systems” [PSiCC2]<br />
(Newnes, 2008).<br />
As shown in Figure 2, QP consists of a universal UML-compliant event<br />
processor (QEP), a portable real-time framework (QF), a tiny preemptive kernel<br />
(QK), and software tracing instrumentation (QS). Current versions of QP<br />
include: QP/C and QP/C++, which require about 4KB of code and a few<br />
hundred bytes of RAM, and the ultra-lightweight QP-nano, which requires only<br />
1-2KB of code and just several bytes of RAM. QP can work with or without a traditional RTOS or OS. In<br />
the simplest configuration, QP can completely replace a traditional RTOS. QP can manage up to 63<br />
concurrently executing tasks structured as state machines (called active objects in UML).<br />
Figure 2: QP components and their relationship with the target hardware, board support package<br />
(BSP), and the application comprised of state machines<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
2 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
1.2 About the QP Port to <strong>PIC24</strong>/<strong>dsPIC</strong><br />
Figure 3 shows the architecture of the QP port to <strong>PIC24</strong>/<strong>dsPIC</strong>. The port takes advantage of the<br />
<strong>PIC24</strong>/<strong>dsPIC</strong> interrupt priority level (IPL) management hardware. In both cooperative and preemptive QP<br />
ports the hardware priority levels are allocated as follows:<br />
1. All QP tasks (active objects) as well as the QP idle loop execute at the lowest IPL of zero.<br />
2. IPL levels 1 through 6 are used for maskable interrupts. These maskable interrupts (IPL 1..6) can call<br />
QP services, such as QF_tick(), QActive_postFIFO(), QF_publish(), and Q_NEW(). The<br />
maskable interrupts can nest on each other, which is the default in <strong>PIC24</strong>/<strong>dsPIC</strong>.<br />
3. This QP port never disables the IPL level 7, which is the Non-Maskable Interrupt (NMI) level. The<br />
NMI-level interrupt(s) cannot call any QP services. But the NMI can trigger a maskable interrupt, as<br />
shown by the dashed arrow in Figure 3. This triggered interrupt of IPL 1..6 can call QP services for<br />
signaling the QP tasks (active objects).<br />
Figure 3: Architecture of the <strong>PIC24</strong>/<strong>dsPIC</strong> port<br />
(IPL = 7) Non-Maskable Interrupt (NMI)<br />
IPL Level 7<br />
(NMI level)<br />
(IPL = 6) User interrupt<br />
(IPL = 5) User interrupt<br />
(IPL = 4) User interrupt<br />
IPL Levels 1-6<br />
(interrupt level)<br />
(IPL = 3) User interrupt<br />
(IPL = 2) User interrupt<br />
(IPL = 1) User interrupt<br />
(QP prio = n) User task (active object)<br />
(QP prio = n-1) User task (active object)<br />
. . .<br />
(QP prio = 2) User task (active object)<br />
IPL Level 0<br />
(task level)<br />
(QP prio = 1) User task (active object)<br />
(QP prio = 0) Idle task<br />
This layered software architecture fits very naturally with the <strong>PIC24</strong>/<strong>dsPIC</strong> hardware. The QP ports (both<br />
cooperative and preemptive) allow nesting of interrupts, which is the default in <strong>PIC24</strong>/<strong>dsPIC</strong>. Both<br />
cooperative and preemptive QP ports can work with the interrupt service routines (ISRs) generated by the<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
3 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
<strong>C30</strong> compiler, although the preemptive version requires special macros to be invoked upon entering and<br />
exiting ISRs.<br />
The interrupt locking policy is implemented very efficiently by means of the DISI instruction (see<br />
Section 3.2). As described in Section 8.2.3 of “<strong>PIC24</strong>F Family Reference Manual” [<strong>PIC24</strong> Ref], “The DISI<br />
instruction only disables interrupts with priority levels 1-6. Priority level 7 interrupts and all trap events still<br />
have the ability to interrupt the CPU when the DISI instruction is active.” This means that interrupts with<br />
IPL of 7 are never disabled and thus run with “zero interrupt latency”.<br />
NOTE: Obviously, true “zero interrupt latency” is technically impossible. The term means here that<br />
the NMI-level interrupt(s) execute with the minimal latency achievable in <strong>PIC24</strong>/<strong>dsPIC</strong> hardware and<br />
that no additional latency is added to the NMI-level interrupt(s) because of interrupt locking in QP.<br />
The use of NMI to achieve “zero interrupt latency” is explained in Section 5.<br />
1.3 What’s Included in the <strong>QDK</strong>-<strong>PIC24</strong>-<strong>dsPIC</strong><br />
This <strong>QDK</strong> provides a basic Board Support Package (BSP) for <strong>PIC24</strong> and <strong>dsPIC</strong> MCUs and two versions<br />
of the Dining Philosopher Problem (DPP) example for each processor. The DPP application is described<br />
in Chapter 7 of [PSiCC2] as well as in the Application Note “Dining Philosopher Problem” [QL AN-DPP<br />
08] (included in the <strong>QDK</strong> distribution):<br />
<br />
<br />
<br />
<br />
The Dining Philosopher Problem example with the cooperative “vanilla” kernel for<br />
<strong>PIC24</strong>FJ128GA010;<br />
The Dining Philosopher Problem example with the preemptive QK” kernel for <strong>PIC24</strong>FJ128GA010<br />
(described in Chapter 10 of [PSiCC2]);<br />
The Dining Philosopher Problem example with the cooperative “vanilla” kernel for<br />
<strong>dsPIC</strong>33FJ256GP710; and<br />
The Dining Philosopher Problem example with the preemptive QK” kernel for <strong>dsPIC</strong>33FJ256GP710<br />
(described in Chapter 10 of [PSiCC2]);<br />
1.4 Licensing QP<br />
The Generally Available (GA) distribution of QP available for download from the www.statemachine.com/downloads<br />
website is offered with the following two licensing options:<br />
<br />
<br />
The GNU General Public License version 2 (GPL) as published by the Free<br />
Software Foundation and appearing in the file GPL.TXT included in the packaging of<br />
every <strong>Quantum</strong> <strong>Leaps</strong> software distribution. The GPL open source license allows<br />
you to use the software at no charge under the condition that if you redistribute the<br />
original software or applications derived from it, the complete source code for your<br />
application must be also available under the conditions of the GPL (GPL Section<br />
2[b]).<br />
One of several <strong>Quantum</strong> <strong>Leaps</strong> commercial licenses, which are designed for<br />
customers who wish to retain the proprietary status of their code and therefore cannot<br />
use the GNU General Public License. The customers who license <strong>Quantum</strong> <strong>Leaps</strong><br />
software under the commercial licenses do not use the software under the GPL and<br />
therefore are not subject to any of its terms.<br />
For more information, please visit the licensing section of our website at: www.statemachine.com/licensing.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
4 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
2 Getting Started<br />
This section describes how to install, build, and use <strong>QDK</strong>-<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong>.<br />
2.1 Software Installation<br />
The <strong>QDK</strong> code is distributed in a ZIP archive (qdk_pic24_dspic-c30_.zip, where stands<br />
for a specific <strong>QDK</strong> version, such as 4.0.04). You can uncompress the archive into any directory. The<br />
installation directory you choose will be referred henceforth as . The following Listing 1 shows the<br />
directory structure and selected files included in the QP distribution. (Please note that the QP directory<br />
structure is described in detail in a separate <strong>Quantum</strong> <strong>Leaps</strong> Application Note: “QP Directory Structure”).<br />
NOTE: Every <strong>QDK</strong> contains only the QP port and example(s) pertaining to the specific MCU and<br />
compiler, but does not include the platform-independent baseline code of QP, which is available for a<br />
separate download from www.state-machine.com/downloads/.<br />
Listing 1: Selected QP directories and files after installing <strong>QDK</strong>-<strong>PIC24</strong>-<strong>dsPIC</strong>-<strong>C30</strong><br />
/<br />
- QP Root Directory<br />
+-doc\<br />
| +-AN_DPP.pdf - Application Note “Dining Philosopher Problem Example”<br />
| +-<strong>QDK</strong>_<strong>PIC24</strong>-<strong>dsPIC</strong>.pdf – This <strong>QDK</strong> Manual “<strong>QDK</strong> <strong>PIC24</strong>-<strong>dsPIC</strong>-<strong>C30</strong>”<br />
|<br />
+-ports/<br />
- QP ports<br />
| +-pic24-dspic\ - Pic24-dspic ports<br />
| | +-vanilla\ - Ports to the non-preemptive “vanilla” kernel<br />
| | | +-mplab-c30\ - Microchip MPLAB <strong>C30</strong> compiler<br />
| | | | +-dbg\ – Debug build<br />
| | | | | +-libqp_24FJ128GA010.a – QP library for <strong>PIC24</strong>FJ128GA010<br />
| | | | | +-libqp_33FJ256GP710.a – QP library for <strong>dsPIC</strong>33FJ256GP710<br />
| | | | +-rel\ – Release build<br />
| | | | | +-libqp_24FJ128GA010.a – QP library for <strong>PIC24</strong>FJ128GA010<br />
| | | | | +-libqp_33FJ256GP710.a – QP library for <strong>dsPIC</strong>33FJ256GP710<br />
| | | | +-spy\ – Spy build<br />
| | | | | +-libqp_24FJ128GA010.a – QP library for <strong>PIC24</strong>FJ128GA010<br />
| | | | | +-libqp_33FJ256GP710.a – QP library for <strong>dsPIC</strong>33FJ256GP710<br />
| | | | +-make_24FJ128GA010.bat – Batch to build QP for <strong>PIC24</strong>FJ128GA010<br />
| | | | +-make_33FJ256GP710.bat – Batch to build QP for <strong>dsPIC</strong>33FJ256GP710<br />
| | | | +-qep_port.h – QEP platform-dependent public include<br />
| | | | +-qf_port.h – QF platform-dependent public include<br />
| | | | +-qs_port.h – QS platform-dependent public include<br />
| | |<br />
| | +-qk\ - QK (<strong>Quantum</strong> Kernel) ports<br />
| | | +-mplab-c30\ - Microchip MPLAB <strong>C30</strong> compiler<br />
| | | | +-dbg\ – Debug build<br />
| | | | | +-libqp_24FJ128GA010.a – QP library for <strong>PIC24</strong>FJ128GA010<br />
| | | | | +-libqp_33FJ256GP710.a – QP library for <strong>dsPIC</strong>33FJ256GP710<br />
| | | | +-rel\ – Release build<br />
| | | | | +-libqp_24FJ128GA010.a – QP library for <strong>PIC24</strong>FJ128GA010<br />
| | | | | +-libqp_33FJ256GP710.a – QP library for <strong>dsPIC</strong>33FJ256GP710<br />
| | | | +-spy\ – Spy build<br />
| | | | | +-libqp_24FJ128GA010.a – QP library for <strong>PIC24</strong>FJ128GA010<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
5 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
| | | | | +-libqp_33FJ256GP710.a – QP library for <strong>dsPIC</strong>33FJ256GP710<br />
| | | | +-make_24FJ128GA010.bat – Batch to build QP for <strong>PIC24</strong>FJ128GA010<br />
| | | | +-make_33FJ256GP710.bat – Batch to build QP for <strong>dsPIC</strong>33FJ256GP710<br />
| | | | +-qep_port.h – QEP platform-dependent public include<br />
| | | | +-qf_port.h – QF platform-dependent public include<br />
| | | | +-qk_port.h – QK platform-dependent public include<br />
| | | | +-qs_port.h – QS platform-dependent public include<br />
|<br />
+-examples\<br />
- subdirectory containing the QP example files<br />
| +-pic24-dspic\ - <strong>PIC24</strong>-<strong>dsPIC</strong> examples<br />
| | +-vanilla\ - Ports to the non-preemptive “vanilla” kernel<br />
| | | +-mplab-c30\ - Microchip MPLAB <strong>C30</strong> compiler<br />
| | | | +-dpp-explorer16_pic24\ - DPP example for Explorer 16 with <strong>PIC24</strong> chip<br />
| | | | | +-dbg\ - directory containing the Debug build<br />
| | | | | | +-dpp-dbg.cof - image of the application<br />
| | | | | | +-dpp-dbg.map - map file of the application<br />
| | | | | +-rel\ - directory containing the Release build<br />
| | | | | +-spy\ - directory containing the Spy build<br />
| | | | | |<br />
| | | | | +-bsp.c - BSP for Explorer 16 with <strong>PIC24</strong>FJ128GA010<br />
| | | | | +-bsp.h - BSP header file<br />
| | | | | +-main.c - the main function<br />
| | | | | +-philo.c - the Philosopher active objects<br />
| | | | | +-dpp.h - the DPP application header file<br />
| | | | | +-table.c - the Table active object<br />
| | | | | +-dpp-dbg.mcp – MPLAB project for Debug configuration<br />
| | | | | +-dpp-rel.mcp – MPLAB project for Release configuration<br />
| | | | | +-dpp-spy.mcp – MPLAB project for Spy configuration<br />
| | | | | +-dpp-explorer16_pic24.mcw – MPLAB workspace for the application<br />
| | | | | +-p24FJ128GA010.gld - Linker script for <strong>PIC24</strong>FJ128GA010<br />
| | | | |<br />
| | | | +-dpp-explorer16_dspic\ - DPP example for Explorer 16 with <strong>dsPIC</strong> chip<br />
| | | | | +-dbg\ - directory containing the Debug build<br />
| | | | | | +-dpp-dbg.cof - image of the application<br />
| | | | | | +-dpp-dbg.map - map file of the application<br />
| | | | | +-rel\ - directory containing the Release build<br />
| | | | | +-spy\ - directory containing the Spy build<br />
| | | | | |<br />
| | | | | +-bsp.c - BSP for Explorer 16 with <strong>dsPIC</strong>33FJ256GP710<br />
| | | | | +-bsp.h - BSP header file<br />
| | | | | +-main.c - the main function<br />
| | | | | +-philo.c - the Philosopher active objects<br />
| | | | | +-dpp.h - the DPP application header file<br />
| | | | | +-table.c - the Table active object<br />
| | | | | +-dpp-dbg.mcp – MPLAB project for Debug configuration<br />
| | | | | +-dpp-rel.mcp – MPLAB project for Release configuration<br />
| | | | | +-dpp-spy.mcp – MPLAB project for Spy configuration<br />
| | | | | +-dpp-explorer16_dspic.mcw – MPLAB workspace for the application<br />
| | | | | +-p33FJ256GP710.gld - Linker script for <strong>dsPIC</strong>33FJ256GP710<br />
| | |<br />
| | +-qk\ - Ports to the preemptive QK kernel<br />
| | | +-mplab-c30\ - Microchip MPLAB <strong>C30</strong> compiler<br />
| | | | +-dpp-qk-explorer16_pic24\ - DPP example for Explorer 16 with <strong>PIC24</strong> chip<br />
| | | | | +-dbg\ - directory containing the Debug build<br />
| | | | | | +-dpp-dbg.cof - image of the application<br />
| | | | | | +-dpp-dbg.map - map file of the application<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
6 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
| | | | | +-rel\ - directory containing the Release build<br />
| | | | | +-spy\ - directory containing the Spy build<br />
| | | | | |<br />
| | | | | +-bsp.c - BSP for Explorer 16 with <strong>PIC24</strong>FJ128GA010<br />
| | | | | +-bsp.h - BSP header file<br />
| | | | | +-main.c - the main function<br />
| | | | | +-philo.c - the Philosopher active objects<br />
| | | | | +-dpp.h - the DPP application header file<br />
| | | | | +-table.c - the Table active object<br />
| | | | | +-dpp-dbg.mcp – MPLAB project for Debug configuration<br />
| | | | | +-dpp-rel.mcp – MPLAB project for Release configuration<br />
| | | | | +-dpp-spy.mcp – MPLAB project for Spy configuration<br />
| | | | | +-dpp-qk-explorer16_pic24.mcw – MPLAB workspace for the application<br />
| | | | | +-p24FJ128GA010.gld - Linker script for <strong>PIC24</strong>FJ128GA010<br />
| | | | |<br />
| | | | +-dpp-qk-explorer16_dspic\ - DPP example for Explorer 16 with <strong>dsPIC</strong> chip<br />
| | | | | +-dbg\ - directory containing the Debug build<br />
| | | | | | +-dpp-dbg.cof - image of the application<br />
| | | | | | +-dpp-dbg.map - map file of the application<br />
| | | | | +-rel\ - directory containing the Release build<br />
| | | | | +-spy\ - directory containing the Spy build<br />
| | | | | |<br />
| | | | | +-bsp.c - BSP for Explorer 16 with <strong>dsPIC</strong>33FJ256GP710<br />
| | | | | +-bsp.h - BSP header file<br />
| | | | | +-main.c - the main function<br />
| | | | | +-philo.c - the Philosopher active objects<br />
| | | | | +-dpp.h - the DPP application header file<br />
| | | | | +-table.c - the Table active object<br />
| | | | | +-dpp-dbg.mcp – MPLAB project for Debug configuration<br />
| | | | | +-dpp-rel.mcp – MPLAB project for Release configuration<br />
| | | | | +-dpp-spy.mcp – MPLAB project for Spy configuration<br />
| | | | | +-dpp-qk-explorer16_dspic.mcw – MPLAB workspace for the application<br />
| | | | | +-p33FJ256GP710.gld - Linker script for <strong>dsPIC</strong>33FJ256GP710<br />
2.2 Building the QP Libraries<br />
All QP components are deployed as libraries that you statically link to your application. The pre-built<br />
libraries for QEP, QF, QK, and QS are provided inside the \ports\pic24-dspic directory. This<br />
section describes steps you need to take to rebuild the libraries yourself.<br />
NOTE: To achieve commonality among different development tools, <strong>Quantum</strong> <strong>Leaps</strong> software does<br />
not use the vendor-specific IDEs, such as the MPLAB IDE, for building the QP libraries. Instead, QP<br />
supports command-line build process based on simple batch scripts.<br />
The code distribution contains the batch file make__.bat for building all the libraries<br />
located in the \ports\pic24-dspic\... directory. For example, to build the debug version of all<br />
the QP libraries for <strong>PIC24</strong>-<strong>dsPIC</strong>, with the MPLAB <strong>C30</strong> compiler, QK kernel, you open a console window<br />
on a Windows PC, change directory to \ports\pic24-dspic\qk\mplab-c30\, and invoke the<br />
batch by typing at the command prompt the following command:<br />
make_24FJ128GA010.bat<br />
For the <strong>PIC24</strong>FJ128GA010 MCU and<br />
make_33FJ256GP710.bat<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
7 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
for the <strong>dsPIC</strong>33FJ256GP710 MCU.<br />
NOTE: You adjust the MCU type by changing the symbol PIC_DEVICE at the top of the<br />
make_.bat file.<br />
The build process should produce the QP libraries in the location: \ports\pic24-dspic\qk\-<br />
mplab-c30\dbg\. The make_24FJ128GA010.bat files assume that the Microchip MPLAB <strong>C30</strong> toolset<br />
has been installed in the directory C:\tools\Microchip\mplab-c30.<br />
NOTE: You need to adjust the symbol MPLAB_<strong>C30</strong> at the top of the batch scripts if you’ve installed<br />
the MPLAB <strong>C30</strong> compiler into a different directory.<br />
In order to take advantage of the QS (“spy”) instrumentation, you need to build the QS version of the QP<br />
library. You achieve this by invoking the make_2800_ml.bat utility with the “spy” target, like this:<br />
make_24FJ128GA010 spy<br />
The make process should produce the QP library in the directory: \ports\pic24-dspic\qk\-<br />
mplab-c30\spy\.<br />
You choose the build configuration by providing a target to the make_24FJ128GA010.bat utility. The<br />
default target is “dbg”. Other targets are “rel”, and “spy”, respectively.<br />
Table 1 Make targets for the Debug, Release, and Spy library versions<br />
Software Version<br />
Build command<br />
Debug (default)<br />
make_<br />
Release<br />
make_ rel<br />
Spy<br />
make_ spy<br />
2.3 Building and Running the Examples<br />
The examples included in this <strong>QDK</strong> are based on the standard DPP application implemented with active<br />
objects (see <strong>Quantum</strong> <strong>Leaps</strong> Application Note: “Dining Philosophers Problem Application” [QL AN-DPP<br />
08] included in this <strong>QDK</strong>). The example directory contains the MPLAB workspaces and project file that<br />
you can load into the MPLAB IDE, as shown in Figure 4.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
8 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
Figure 4: The MPLAB IDE with the DPP example<br />
Program<br />
target device<br />
button<br />
Select project<br />
for the build<br />
configuration<br />
2.3.1 Building the Example Projects in the MPLAB IDE<br />
1. Connect the Explorer 16 board to the MPLAB ICD 2 In-Circuit debugger and to the PC as described<br />
in the Quick Start Guide.<br />
2. Connect the USB cable between the MPLAB ICD 2 In-Circuit Debugger and the PC.<br />
3. Open the MPLAB IDE and open the project dpp-dbg.mcp (located in \examples\pic18\-<br />
vanilla\mplab-c18\dpp-picdem2plus\). Figure 4 shows the screen shot of the MPLAB IDE after<br />
opening the project.<br />
4. Build the project by select Project->Make menu or by pressing F10.<br />
2.3.2 Programming the Example to Run under the MPLAB Debugger<br />
After a successful build, you can load the program to the target device by pressing the “Program Target<br />
Device” button, which is shown in Figure 4.<br />
2.3.3 Programming the Example to Run Standalone<br />
The DPP example application comes with the dpp-rel.mcp project, which is intended for standalone<br />
execution on the Explorer 16 board without the debugger. Once you activate and build this project in the<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
9 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
MPLAB IDE, you need to de-select the ICD 2 as the Debugger and select ICD 2 as the Programmer (you<br />
cannot configure ICD 2 as a programmer and debugger simultaneously). To program the code into the<br />
<strong>PIC24</strong>/<strong>dsPIC</strong> device, you click the “Program Target Device” icon. After the programming is done, you can<br />
release the device out of reset by clicking the appropriate icon. You can also disconnect the ICD 2<br />
completely and power-cycle the board. The programmed DPP application should start running<br />
standalone.<br />
2.3.4 Collecting the QS software trace<br />
QS (QP-Spy) is a software tracing facility built into all QP components and also available to the<br />
Application code. QS allows you to gain unprecedented visibility into your application by selectively<br />
logging almost all interesting events occurring within state machines, the framework, the kernel, and your<br />
application code. QS software tracing is minimally intrusive, offers precise time-stamping, sophisticated<br />
runtime filtering of events, and good data compression (see Chapter 11 in [PSiCC2]).<br />
The dpp-spy.mcp project contains the Spy configuration that enables the QS software tracing and links<br />
with the Spy-versions of the QP libraries (see Listing 1). You select the Spy configuration by activating the<br />
dpp-spy.mcp project in the MPLAB IDE (see Figure 4).<br />
Before you download and start the DPP-Spy configuration to the Explorer 16 board, you should connect<br />
the board’s build-in DB9 connector to your workstation via a straight serial cable. Figure 5 shows the<br />
QSPY output received from the DPP-Spy configuration.<br />
NOTE: Please note that only a small subset of the QSPY records are allowed through the QS filters.<br />
Please refer to the source code (bsp.c) and the “QSPY Reference Manual” [QSPY 08] for more<br />
information about using the QS filters.<br />
You launch the QSPY utility on a Windows PC as follows. Change the directory to the QSPY host utility<br />
\tools\qspy\win32\mingw\rel and execute:<br />
qspy –cCOM1 –b38400 –O2 –F2 –S2 –E1 –Q1 –P1 –B1<br />
The meaning of the parameters is as follows:<br />
-cCOM1 specifies COM port (change to actual COM port number you’re using)<br />
-b38400 specifies the baud rate (depends on the oscillator frequency of your board, see Section 7)<br />
-O2 specifies the size of an object pointer to 2 bytes<br />
-F2 specifies the size of a function pointer to 2 bytes.<br />
-E1 specifies the size of an event to 1 byte (an event may have up to 255 bytes).<br />
-Q1 specifies the size of a memory queue counter to 1 byte (a event queue can fit up to 255 events).<br />
-P1 specifies the size of a memory pool counter to 1 byte (a memory pool can manage up to 255 memory<br />
blocks).<br />
-B1 specifies the size of a memory block to 1 byte (a memory pool can manage block up to 255 bytes).<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
10 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
Figure 5: QSpy output from the DPP-QSpy configuration<br />
“Dictionary”<br />
trace records<br />
Time stamp<br />
column<br />
State entry<br />
Transition<br />
Internal<br />
Transition<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
11 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
3 Non-Preemptive “Vanilla” Port<br />
The “vanilla” port shows how to use QP on a bare metal <strong>PIC24</strong>-<strong>dsPIC</strong>-based system with the<br />
cooperative “vanilla” kernel. In this version you’re using the non-preemptive kernel built-into the QF<br />
framework and your’e not using the QK component.<br />
3.1 The qep_port.h Header File<br />
The QEP header file for the <strong>PIC24</strong>/<strong>dsPIC</strong> port is located in \ports\pic24-dspic\vanilla\mplabc30\qep_port.h.<br />
Listing 2 shows the qep_port.h header file for <strong>PIC24</strong>/<strong>dsPIC</strong>.<br />
NOTE: The QP port to the cooperative “Vanilla” kernel qep_port.h is generic and should not need<br />
to change for other <strong>PIC24</strong>/<strong>dsPIC</strong> applications.<br />
Listing 2: qep_port.h header file for the cooperative QP configuration and MPLAB-<strong>C30</strong> compiler<br />
#ifndef qep_port_h<br />
#define qep_port_h<br />
(1) #define Q_SIGNAL_SIZE 2<br />
/* 2-byte signal space (64K signals) */<br />
/* Exact-width types. WG14/N843 C99 Standard, Section 7.18.1.1 */<br />
(2) typedef signed char int8_t;<br />
typedef signed int int16_t;<br />
typedef signed long int32_t;<br />
typedef unsigned char uint8_t;<br />
typedef unsigned int uint16_t;<br />
typedef unsigned long uint32_t;<br />
#include "qep.h" /* QEP platform-independent public interface */<br />
#endif /* qep_port_h */<br />
(1) The macro Q_SIGNAL_SIZE defines the size (in bytes) of event signals. The allowed values are 1, 2,<br />
or 4 bytes. Here the value of Q_SIGNAL_SIZE is set to 2, which means that the QP application can<br />
use up to 64K different signals.<br />
(2) The C99-standard exact-width integer types are defined explicitly, because the MPLAB-<strong>C30</strong><br />
compiler does not provide the standard header file.<br />
3.2 The qf_port.h Header File<br />
The QF header file for the <strong>PIC24</strong>/<strong>dsPIC</strong> port is located in \ports\pic24-dspic\ vanilla\mplabc30\qf_port.h.<br />
This file specifies the interrupt locking/unlocking policy (QF critical section) as well as<br />
the configuration constants for QF (see Chapter 8 in [PSiCC2]).<br />
The most important porting decision you need to make in the qf_port.h header file is the policy for<br />
locking and unlocking interrupts. The <strong>PIC24</strong>/<strong>dsPIC</strong> CPU allows using the simplest “unconditional interrupt<br />
unlocking” policy (see Section 7.3.2 of the book “Practical UML Statecharts in C/C++, Second Edition”<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
12 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
[PSiCC2]), because <strong>PIC24</strong>/<strong>dsPIC</strong> can mask interrupt groups (INT1 - INT12) and individual interrupts in<br />
the Peripheral Interrupt Expansion (PIE) module. Listing 3 shows the qf_port.h header file for<br />
<strong>PIC24</strong>/<strong>dsPIC</strong>.<br />
Listing 3: The qf_port.h header file for <strong>PIC24</strong>/<strong>dsPIC</strong>.<br />
/* The maximum number of active objects in the application, see NOTE01 */<br />
(1) #define QF_MAX_ACTIVE 8<br />
(2) #define QF_EVENT_SIZ_SIZE 1<br />
(3) #define QF_EQUEUE_CTR_SIZE 1<br />
(4) #define QF_MPOOL_SIZ_SIZE 1<br />
(5) #define QF_MPOOL_CTR_SIZE 1<br />
(6) #define QF_TIMEEVT_CTR_SIZE 2<br />
/* QF interrupt disable/enable, see NOTE02 */<br />
(7) #define QF_INT_DISABLE() __asm__ volatile("disi #0x3FFF")<br />
(8) #define QF_INT_ENABLE() __asm__ volatile("disi #0x0000")<br />
/* QF critical section entry/exit, see NOTE02 */<br />
(9) /* QF_CRIT_STAT_TYPE not defined: unconditional interrupt unlocking" policy */<br />
(10) #define QF_CRIT_ENTRY(dummy) __asm__ volatile("disi #0x3FFF")<br />
(11) #define QF_CRIT_EXIT(dummy) __asm__ volatile("disi #0x0000")<br />
#include "qep_port.h" /* QEP port */<br />
#include "qvanilla.h" /* "Vanilla" cooperative kernel */<br />
#include "qf.h" /* QF platform-independent public interface */<br />
(1) The QF_MAX_ACTIVE specifies the maximum number of active object priorities in the application.<br />
You always need to provide this constant in the QF port. Here, QF_MAX_ACTIVE is set to 8 to<br />
conserve some RAM, but you can increase this value up to 63 inclusive.<br />
(2-5) These object sizes in QF are all set to 1, meaning that uint8_t will be used to represent the various<br />
object sizes.<br />
NOTE: Please refer to Chapter 8 of [PSiCC2] for discussion of all configurable QF parameters.<br />
(6) These time event counter is configured to 2-bytes, meaning that timeouts of up to 65535 clock ticks<br />
can be handled in this QF port.<br />
(7) The interrupt disable macro resolves to the single instruction DISI #0x3FFF, which disables<br />
interrupts for 16383 instruction cycles. The number of cycles is much longer than any actual critical<br />
section in QP.<br />
NOTE: The DISI instruction only disables interrupts with priority levels 1-6. Priority level 7 interrupts<br />
and all trap events still have the ability to interrupt the CPU when the DISI instruction is active. This<br />
means that from the perspective of QP, the level 7 interrupts are treated as non-maskable interrupts<br />
(NMIs). Such non-maskable interrupts cannot call any QP services. In particular, they cannot post<br />
events (see also Section 5).<br />
(8) The DISI #0 instruction is then used to unconditionally unlock the interrupts at the end of the critical<br />
section.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
13 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
(9) The QF_CRIT_STAT_TYPE macro is not defined, which means that the simple policy of “unconditional<br />
interrupt disabling and enabling” is applied (see Section 7.3 in [PSiCC2]).<br />
(10) The critical section entry macro disables interrupts (see step (7)).<br />
(11) The critical section exit macro unconditionally enables interrupts (see step (8)).<br />
3.3 ISRs in the Non-Preemptive “Vanilla” Configuration<br />
The MPLAB <strong>C30</strong> compiler supports writing interrupts in C. In the non-preemptive “vanilla” port, the ISRs<br />
are identical as in the simplest of all “superloop” (main+ISRs), and there is nothing QP-specific in the<br />
structure of the ISRs. The only QP-specific requirement is that you provide a periodic system clock tick<br />
ISR and you invoke QF_tick() in it. The ISRs are located in the bsp.c file found in the application<br />
directory.<br />
NOTE: The non-preemptive “vanilla” kernel allows interrupts nesting, as the interrupt preemptions<br />
are handled entirely by the <strong>PIC24</strong>/<strong>dsPIC</strong> hardware.<br />
Listing 4: The system tick interrupt calling QF_tick() function to manage armed time events.<br />
(1) void __attribute__((__interrupt__,<br />
(2) auto_psv))<br />
(3) _T2Interrupt(void)<br />
{<br />
(4) _T2IF = 0; /* clear Timer 2 interrupt flag */<br />
#ifdef Q_SPY<br />
(5) l_tickTime += BSP_TMR2_PERIOD; /* account for TMR2 overflow */<br />
#endif<br />
(6) QF_TICK(&l_T2Interrupt); /* handle all armed time events in QF */<br />
}<br />
/* perform other work, if necessary */<br />
(1) In the MPLAB <strong>C30</strong> The ISR must be declared with the attribute __interrupt__.<br />
(2) The auto_psv attribute will cause the compiler to preserve the previous contents of PSVPAG and<br />
set it to section .const. Upon exit, the previous value of PSVPAG will be restored. Alternatively, if<br />
the ISR does not access the PSVPAG access, the attribute no_auto_psv can be used (see Section<br />
8.10 in [MPLAB <strong>C30</strong>] for more details).<br />
NOTE: If you don’t explicitly specify the no_auto_psv attribute, the compiler will err on the safe side<br />
and will implicitly insert the auto_psv atribute.<br />
(3) Every ISR must be a void (void) function.<br />
(4) Most ISRs in the <strong>PIC24</strong>/<strong>dsPIC</strong> architecture must explicitly clear the interrupt flag for the interrupt<br />
source.<br />
(5) When software tracing is enabled, the ISR updates the 32-bit time stamp at tick, which is used to<br />
provide a 32-bit time stamp for the trace events.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
14 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
(6) The system clock tick ISR must invoke QF_TICK(), and can also perform other actions, if<br />
necessary. The function QF_TICK() cannot be reentered, that is, it necessarily must run to<br />
completion and return before it can be called again. This requirement is automatically fulfilled,<br />
because the same interrupt priority cannot preempt the running priority in <strong>PIC24</strong>/<strong>dsPIC</strong>.<br />
3.3.1 PSV Usage in ISRs<br />
This QP port to <strong>PIC24</strong>/<strong>dsPIC</strong> can work with ISRs that preserve the PSVPAG register as well as ISRs that<br />
don’t. The preserving of the PSVPAG register is controlled by the attributes auto_psv and no_auto_psv<br />
that the MPLAB <strong>C30</strong> compiler supports for this purpose (see Listing 4(2)).<br />
If an ISR references const variables or string literals using the constants-in-code memory model, the<br />
auto_psv attribute should be added to the ISR definition. This attribute will cause the compiler to<br />
preserve the previous contents of PSVPAG and set it to section .const. Upon exit, the previous value of<br />
PSVPAG will be restored. Alternatively, if the ISR does not access the PSVPAG access, the attribute<br />
no_auto_psv can be used (see Section 8.10 in [MPLAB <strong>C30</strong>] for more details). The no_auto_psv<br />
attribute results in slightly faster ISR with one register less saved on the stack.<br />
3.3.2 Shadow Registers Usage in ISRs<br />
The non-preemptive “vanilla” QP port to <strong>PIC24</strong>/<strong>dsPIC</strong> can work with ISRs that preserve the context in the<br />
shadow registers available in the <strong>PIC24</strong>/<strong>dsPIC</strong> CPU. To request the compiler to use the fast context save<br />
(using the push.s and pop.s instructions), tag the function with the __shadow__ attribute. For example:<br />
void __attribute__((__interrupt__, __shadow__)) _T2Interrupt(void);<br />
NOTE: Generally, shadow registers should be used at one IPL level only (typically the highest).<br />
When interrupt nesting is allowed, which is the default in the <strong>PIC24</strong>/<strong>dsPIC</strong> architecture, the shadow<br />
registers can get corrupted if they are used by interrupts of two or more different IPL levels.<br />
3.3.3 Assigning/Changing Interrupt Priorities in Software<br />
Each ISR in <strong>PIC24</strong>/<strong>dsPIC</strong> runs at a pre-configured IPL level. The default IPL level assigned to all ISRs out<br />
of reset is 4. It is strongly recommended, however, to assign priority to each used interrupt explicitly<br />
before enabling the interrupt. For example, the highlighted code in Listing 5 shows assigning the priority<br />
to the Timer2 interrupt in function QF_onStartup():<br />
Listing 5: Configuring interrupts in function QF_onStartup() (file bsp.c)<br />
#define TIMER2_ISR_PRIO 4<br />
. . .<br />
void QF_onStartup(void) { /* entered with interrupts locked */<br />
T2CON = 0; /* Use Internal Osc (Fcy), 16 bit mode, prescaler = 1 */<br />
TMR2 = 0; /* Start counting from 0 and clear the prescaler count */<br />
PR2 = BSP_TMR2_PERIOD - 1; /* set the timer period */<br />
_T2IP = TIMER2_ISR_PRIO; /* set Timer 2 interrupt priority */<br />
_T2IF = 0; /* clear the interrupt for Timer 2 */<br />
_T2IE = 1; /* enable interrupt for Timer 2 */<br />
T2CONbits.TON = 1; /* start Timer 2 */<br />
}<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
15 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
3.4 QP Idle Loop Customization in QF_onIdle()<br />
The cooperative “vanilla” kernel can very easily detect the situation when no events are available, in<br />
which case QF_run() calls the QF_onIdle() callback. You can use QF_onIdle() to suspended the CPU<br />
to save power, if your CPU supports such a power-saving mode. Please note that QF_onIdle() is called<br />
repetitively from the event loop whenever the event loop has no more events to process, in which case<br />
only an interrupt can provide new events. The QF_onIdle() callback is called with interrupts locked,<br />
because the determination of the idle condition might change by any interrupt posting an event.<br />
The <strong>PIC24</strong>-<strong>dsPIC</strong> CPU supports several power-saving levels (consult the data sheets of particular<br />
<strong>PIC24</strong>/<strong>dsPIC</strong> devices for details). The following piece of code shows the QF_onIdle() callback that puts<br />
Pic24-dspic DSP into the IDLE power-saving mode. Please note that Pic24-dspic architecture allows for<br />
an atomic setting the low-power mode and enabling interrupts at the same time.<br />
Listing 6: QF_onIdle() for the non-preemptive (“vanilla”) QP port to <strong>PIC24</strong>/<strong>dsPIC</strong><br />
(1) void QF_onIdle(void) { /* entered with int disabled, NOTE01 */<br />
(2) LED_ON (IDLE_LED); /* blink the IDLE LED, see NOTE02 */<br />
(3) LED_OFF(IDLE_LED);<br />
(4) #ifdef Q_SPY<br />
. . . /* output Q-SPY trace data to the UART (see Section 7) */<br />
(5) #elif defined NDEBUG<br />
(6) __asm__ volatile("disi #0x0001");<br />
(7) Idle(); /* transition to Idle mode, see NOTE03 */<br />
#else<br />
(8) QF_INT_ENABLE(); /* enable interrupts, see NOTE01 */<br />
#endif<br />
}<br />
(1) The QF_onIdle() callback is always called with interrupts disabled to prevent any race condition<br />
between posting events from ISRs and transitioning to the sleep mode.<br />
(2-3) The LED D10 on the Explorer 16 board is used to visualize the idle loop activity. The LED is toggled<br />
on and off, which causes the LED to “glow” at the intensity proportional to the rate of invocations of<br />
the idle callback. The idle LED is toggled with interrupts still disabled, which means that the intensity<br />
of the LED “glow” is not skewed by preemptions from interrupts or tasks.<br />
(4) If QSPY software tracing is enabled, the QF_onIdle() callback tries to output one byte from the<br />
trace buffer to the SCI. See Section 7 for more information about QSPY software tracing.<br />
(5) The low-power mode stops the CPU clock, so it can interfere with the debugger. Here, the lowpower<br />
mode is activated only in the Release build configuration when the macro NDEBUG is<br />
defined.<br />
(6) Interrupts are locked for just one following machine instruction, which ensures that interrupts will<br />
unlock after the IDLE mode.<br />
(7) The IDLE mode is activated by executing the IDLE instruction.<br />
NOTE: The <strong>PIC24</strong>-<strong>dsPIC</strong> allows for atomic transition to the Idle or Sleep modes with interrupts<br />
locked, as it should be done for a deterministic transition to the low-power mode (see [Samek 07a]).<br />
(8) When the idle/sleep mode is not used, interrupts are simply enabled.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
16 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
4 Preemptive Configuration with QK<br />
The QP port to <strong>PIC24</strong>/<strong>dsPIC</strong> with the preemptive kernel (QK) is similar to the “vanilla” port. In particular,<br />
the interrupt locking/unlocking policy is the same, and the BSP is almost identical, except for some extra<br />
considerations for the ISRs.<br />
4.1 QK-specific ISR Entry and Exit Macros (file qk_port.h)<br />
The preemptive QP port to <strong>PIC24</strong>/<strong>dsPIC</strong> also allows writing ISRs in C, so no explicit assembly<br />
programming is necessary. However, the preemptive QK kernel requires (as all preemptive kernels do)<br />
notifying the kernel about entering and exiting the ISR, so that the kernel can avoid context switching<br />
inside ISRs, but knows when to perform asynchronous preemptions when the last nested ISR returns to<br />
the task level.<br />
While working with the compiler-generated ISRs, the biggest problem for a preemptive kernel is to<br />
reliably detect that a given ISR is not nesting on top of another ISR, so that only the last interrupt<br />
returning to the task level performs context switch, if necessary.<br />
NOTE: Unlike most other processors, <strong>PIC24</strong>/<strong>dsPIC</strong> does not disable interrupts upon the entry to the<br />
interrupt service routine. <strong>PIC24</strong>/<strong>dsPIC</strong> merely raises the current IPL to the level of currently serviced<br />
interrupt. This means that the currently serviced interrupt can be preempted by a higher-level<br />
interrupt even at the very first instruction. Consequently, incrementing the interrupt nesting counter<br />
(QK_intNest_) is not a reliable way of accounting for interrupt nesting, because it can miss an<br />
interrupt nesting level no matter how early in the ISR it is performed.<br />
For <strong>PIC24</strong>/<strong>dsPIC</strong>, a reliable way of detecting nesting of interrupts is to check the preempted status<br />
register SR, which is saved on the interrupt stack as shown in Figure 6. Only if the preempted IPL<br />
(bits SR) is non-zero, the current interrupt preempts the task level and performing a context switch<br />
is allowed.<br />
Figure 6: Interrupt stack frame for <strong>PIC24</strong>/<strong>dsPIC</strong><br />
Low memory<br />
High memory<br />
15<br />
PC<br />
SR<br />
PC<br />
Register saved by the <strong>C30</strong> compiler<br />
Register saved by the <strong>C30</strong> compiler<br />
…<br />
Register saved by the <strong>C30</strong> compiler<br />
0<br />
IPL3 status bit<br />
(CORCON).<br />
Stack pointer<br />
(w15)<br />
While checking the stacked SR is quite easy in assembly, it is much trickier for the compiler-generated<br />
ISRs. The compiler generates different stack frames depending on the actually clobbered registers in the<br />
body of the ISR as well as the attributes of the ISR and even the optimization level used to compile the<br />
code. Consequently, it is difficult to know the offset of the stacked SR from the current stack pointer w15<br />
that is accessible inside the ISR, (again see Figure 6).<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
17 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
The solution used in this QK port is to use the __preprologue__ attribute of the compiler-generated ISR<br />
to check the stacked SR before the compiler pushes any registers to the stack and alters the stack<br />
pointer. The IPL extracted from the stacked SR is then stored in the global variable QK_intNest_, which<br />
in this case is not used as the interrupt nesting counter, but rather as a bitmask (a set) of preempted IPL<br />
levels. More precisely, the QK-specific ISR entry code added before any compiler-generated code checks<br />
the stacked IPL level in SR and sets the bit corresponding to the preempted IPL in the<br />
QK_intNest_ bitmask, as illustrated in Figure 7. In the QK-specific ISR exit code, the current IPL is<br />
extracted from the SR and the bit corresponding to the current IPL is cleared in the QK_intNest_<br />
bitmask.<br />
Figure 7: Storing the preempted IPLs in the QK_intNest_ bitmask<br />
Bit number<br />
7<br />
6<br />
5<br />
4<br />
3<br />
2<br />
1<br />
0<br />
0<br />
0<br />
1<br />
0<br />
0<br />
0<br />
1<br />
1<br />
QK_intNest_<br />
Unused level (IPL = 7)<br />
Preempted IPL = 5<br />
Preempted IPL = 1<br />
Preempted IPL = 0<br />
The QK-specific interrupt entry and exit sequence defined in the header file qk_port.h shown in Listing 7<br />
demonstrates one possible solution to this problem. The QK port header file for the <strong>PIC24</strong>/<strong>dsPIC</strong> port is<br />
located in \ports\pic24-dspic\qk\mplab-c30\qk_port.h. The upcoming Section “ISRs with the<br />
Preemptive QK Kernel” explains how to use the macros QK_ISR_ENTRY() / QK_ISR_EXIT() and how<br />
these macros work at the assembly level.<br />
Listing 7: qk_port.h header file for the preemptive QP port with QK<br />
/* QK interrupt entry and exit */<br />
(1) #define QK_ISR(psv_) \<br />
(2) void __attribute__((__interrupt__(__preprologue__( \<br />
"push RCOUNT \n" \<br />
"push.d w0 \n" \<br />
"mov.w [w15-8],w0 \n" \<br />
"lsr.w w0,#13,w1 \n" \<br />
"mov.w #1,w0 \n" \<br />
"sl w0,w1,w0 \n" \<br />
"ior.b _QK_intNest_\n" \<br />
"bra .+6 ")) \<br />
, psv_))<br />
(3) #define QK_ISR_EXIT() do { \<br />
register uint16_t this_sr; \<br />
__asm__ volatile ( \<br />
"mov.w SR,%0 \n" \<br />
"lsr %0,#5,w0 \n" \<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
18 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
"and.w w0,#7,w0 \n" \<br />
"mov.w #1,w1 \n" \<br />
"sl w1,w0,w0 \n" \<br />
"ior.b #1,w0 \n" \<br />
"com.b w0,w0 \n" \<br />
"disi #0x3FFF \n" \<br />
"and.b _QK_intNest_" : "=r"(this_sr) : : "w0", "w1"); \<br />
if (QK_intNest_ == 0) { \<br />
uint8_t p = QK_schedPrio_(); \<br />
if (p != (uint8_t)0) { \<br />
__asm__ volatile ("clr.b SR"); \<br />
QK_sched_(p); \<br />
__asm__ volatile ("mov.w %0,SR" : : "r"(this_sr)); \<br />
} \<br />
} \<br />
__asm__ volatile ("disi #0x0000"); \<br />
} while (0);<br />
#include "qk.h" /* QK platform-independent public interface */<br />
(1) The macro QK_ISR() defines the ISR attributes, as required by the MPLAB <strong>C30</strong> compiler. The<br />
macro is designed to be before the actual ISR signature, as will be illustrated in Listing 8. The<br />
argument psv_ controls PSV usage in the ISR and can be either auto_psv or no_auto_psv (see<br />
Section 3.3.1).<br />
(2) The macro QK_ISR() then uses the attribute __preprologue__ to define the specific interrupt entry<br />
sequence in assembly, which the compiler copies verbatim before any generated ISR code.<br />
(3) The macro QK_ISR_EXIT() must be invoked as the last instruction of every ISR of IPL 1..6. Again,<br />
the usage of this macro will be illustrated in Listing 8.<br />
4.2 ISRs with the Preemptive QK Kernel<br />
Even though the QK interrupt entry/exit macros are somewhat involved, their use is actually very simple<br />
and enables very straightforward coding ISRs in C, without any need to use assembly. The following<br />
Listing 8 shows how to write ISRs for the preemptive QK kernel:<br />
NOTE: The QK interrupt entry and exit macros QK_ISR() and QK_ISR_EXIT() work only with the<br />
optimization level O1 or higher (O2, O3, or Os) in the <strong>C30</strong> compiler. The MPLAB <strong>C30</strong> compiler will<br />
report a compile-time error “'asm' operand requires impossible reload” when no optimization is<br />
used.<br />
Listing 8: T2Interrupt ISR for QK<br />
(1) QK_ISR(no_auto_psv) _T2Interrupt() {<br />
_T2IF = 0; /* clear Timer 2 interrupt flag */<br />
#ifdef Q_SPY<br />
(2) l_tickTime += BSP_TMR2_PERIOD; /* account for TMR2 overflow */<br />
#endif<br />
QF_TICK(&l_T2Interrupt); /* handle all armed time events in QF */<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
19 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
(3) QK_ISR_EXIT(); /* inform QK about exiting the ISR */<br />
}<br />
(1) The macro QK_ISR() is used before the ISR signature. Here the PSV control is set to no_auto_ps,<br />
which avoids saving and restoring the PSVPAG register. If your ISR body references const variables<br />
or string literals using the constants-in-code memory model, you need to define this argument to<br />
auto_psv.<br />
(2) When software tracing is enabled, the ISR updates the 32-bit time stamp at tick, which is used to<br />
provide a 32-bit time stamp for the trace events.<br />
(3) The macro QK_ISR_EXIT() is invoked at the end of every ISR to perform the asynchronous context<br />
switch, if necessary.<br />
4.2.1 Explanation of ISR code for the preemptive QK kernel<br />
Listing 9 shows the disassembled code for the _T2Interrupt ISR from Listing 8. The highlighted code is<br />
generated by the macros QK_ISR() and QK_ISR_EXIT(). The explanation section immediately following<br />
the listing clarifies the most important points.<br />
Listing 9: Disassembled code for T2Interrupt ISR<br />
72: QK_ISR(no_auto_psv) _T2Interrupt() {<br />
(1) 009F8 F80036 push.w 0x0036<br />
(2) 009FA BE9F80 mov.d 0x0000,[0x001e++]<br />
(3) 009FC 97B84F mov.w [0x001e-8],0x0000<br />
(4) 009FE DE00CD lsr 0x0000,#13,0x0002<br />
(5) 00A00 200010 mov.w #0x1,0x0000<br />
(6) 00A02 DD0001 sl 0x0000,0x0002,0x0000<br />
(7) 00A04 B76993 ior.b 0x0993<br />
(8) 00A06 370002 bra 0x000a0c<br />
(9) 00A08 F80036 push.w 0x0036<br />
(10) 00A0A BE9F80 mov.d 0x0000,[0x001e++]<br />
(11) 00A0C BE9F82 mov.d 0x0004,[0x001e++]<br />
(12) 00A0E BE9F84 mov.d 0x0008,[0x001e++]<br />
(13) 00A10 BE9F86 mov.d 0x000c,[0x001e++]<br />
(14) 00A12 781F88 mov.w 0x0010,[0x001e++]<br />
73: _T2IF = 0; /* clear Timer 2 interrupt flag */<br />
00A14 A9E084 bclr.b 0x0084,#7<br />
74:<br />
75: #ifdef Q_SPY<br />
76: l_tickTime += BSP_TMR2_PERIOD; /* TMR2 overflow */<br />
77: #endif<br />
78:<br />
79: QF_tick(); /* handle all armed time events in QF */<br />
00A16 07027E rcall 0x000f14<br />
80:<br />
81: QK_ISR_EXIT(); /* inform QK about exiting the ISR */<br />
(15) 00A18 800218 mov.w 0x0042,0x0010<br />
(16) 00A1A DE4045 lsr 0x0010,#5,0x0000<br />
(17) 00A1C 600067 and.w 0x0000,#7,0x0000<br />
(18) 00A1E 200011 mov.w #0x1,0x0002<br />
(19) 00A20 DD0800 sl 0x0002,0x0000,0x0000<br />
(20) 00A22 B34010 ior.b #0x1,0x0000<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
20 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
(21) 00A24 EAC000 com.b 0x0000,0x0000<br />
(22) 00A26 FC3FFF disi #16383<br />
(23) 00A28 B66993 and.b 0x0993<br />
(24) 00A2A E24993 cp0.b 0x0993<br />
(25) 00A2C 3A0003 bra nz, 0x000a34<br />
(26) 00A2E EF6042 clr.b 0x0042<br />
(27) 00A30 070178 rcall 0x000d22<br />
(28) 00A32 880218 mov.w 0x0010,0x0042<br />
(29) 00A34 FC0000 disi #0<br />
82: }<br />
(30) 00A36 78044F mov.w [--0x001e],0x0010<br />
(31) 00A38 BE034F mov.d [--0x001e],0x000c<br />
(32) 00A3A BE024F mov.d [--0x001e],0x0008<br />
(33) 00A3C BE014F mov.d [--0x001e],0x0004<br />
(34) 00A3E BE004F mov.d [--0x001e],0x0000<br />
(35) 00A40 F90036 pop.w 0x0036<br />
(36) 00A42 064000 retfie<br />
(1) The RCOUNT register (at address 0x36) is pushed to the stack. This is done to establish the same<br />
stack layout as the MPLAB <strong>C30</strong> compiler does for all ISRs.<br />
(2) The register pair w0-w1 is pushed to the stack. Now these registers can be clobbered.<br />
(3) The stacked SRCORCONPC bits (see Figure 6) are loaded from the stack to w0.<br />
Please note that after pushing RCOUNT and w0,w1 pair, this information is 8 words away from the<br />
current stack pointer in w15.<br />
(4) The register w0 is left-shifted by 13 bits, so that the stacked IPL ends up in w1.<br />
(5) The register w1 is compared with zero.<br />
(6) The stacked IPL indicates that an ISR has been preempted, so the corresponding bit in the<br />
QK_intNest_ bitmask needs to be set. Here the bitmask (1
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
4.11 “Function Call Conventions” in [MPLAB-<strong>C30</strong>]). However, these 9 registers are still less than a<br />
half of all <strong>PIC24</strong>/<strong>dsPIC</strong> registers that are saved by most other traditional preemptive kernels/RTOSs<br />
such as MicroC/OS-II or FreeRTOS.org. The traditional RTOSs must save the whole register file of<br />
the CPU, because the kernel cannot optimize the saved context for each individual ISR.<br />
(15) The QK-specific ISR exit starts with placing the current SR into the this_sr local variable, which<br />
the <strong>C30</strong> compiler decided to place in the w8 register.<br />
(16) The IPL bits from the current SR are right-shifted into w0.<br />
(17) All other SR bits, except the IPL bits in w0 are masked off in w0.<br />
(18-19) The bitmask (1
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
The following Listing 10 shows an example implementation of QK_onIdle() for the <strong>PIC24</strong>/<strong>dsPIC</strong>. Please<br />
note that the ilde-LED is turned on and off in a critical section (so that the intensity of the LED does not<br />
include any preemptions by interrupts and tasks). The transition to the Idle mode happens in a very<br />
straightforward way, because transition to idle mode is always safe under a preemptive priority-based<br />
kernel, such as QK.<br />
void QK_onIdle(void) {<br />
Listing 10: QK_onIdle() callback for <strong>PIC24</strong>/<strong>dsPIC</strong>.<br />
QF_INT_DISABLE();<br />
LED_ON (IDLE_LED); /* blink the IDLE LED, see NOTE01 */<br />
LED_OFF(IDLE_LED);<br />
QF_INT_ENABLE();<br />
#ifdef Q_SPY<br />
while (U2STAbits.UTXBF == 0U) { /* TX Buffer not full */<br />
uint16_t b;<br />
QF_INT_DISABLE();<br />
b = QS_getByte();<br />
QF_INT_ENABLE();<br />
if (b == QS_EOD) { /* End-Of-Data reached */<br />
break; /* break out of the loop */<br />
}<br />
U2TXREG = (uint8_t)b; /* stick the byte to TXREG for transmission */<br />
}<br />
#elif defined NDEBUG<br />
Idle(); /* transition to Idle mode */<br />
#endif<br />
}<br />
4.4 Testing QK Preemption Scenarios<br />
The technique described in this section will allow you to use the MPLAB debugger to trigger an interrupt<br />
at any machine instruction and observe the preemptions it causes. The interrupt used for the testing<br />
purposes is the GPIOA interrupt (INTID == 0). The ISR for this interrupt is shown below:<br />
The DPP example application for the preemptive QK kernel includes special ISR (INT0 ISR) for<br />
convenient testing of various preemption scenarios, defined as follows:<br />
#define INT0_ISR_PRIO 6<br />
QK_ISR(auto_psv) _INT0Interrupt() {<br />
static QEvent const eat_evt = { EAT_SIG, 0 };<br />
_INT0IF = 0;<br />
QACTIVE_POST(AO_Table, &eat_evt, &l_INT0Interrupt);<br />
QK_ISR_EXIT(); /* inform QK about exiting the ISR */<br />
}<br />
The INT0 ISR, is assigned priority 6, which is higher than the priority of the system clock tick ISR.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
23 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
Figure 8 shows how to trigger the INT0 interrupt from the MPLAB debugger. From the debugger you need<br />
to first open the “File Registers” window (menu View | File Registers) as shown in Figure 8. You scroll to<br />
the IFS0 register at address 0x0084. To trigger the INT0 interrupt you need to write 1 to the leastsignificant<br />
bit (bit #0) of the IFS0 register.<br />
The general testing strategy is to break into the application at an interesting place for preemption, set<br />
breakpoints to verify which path through the code is taken, and trigger the INT0 interrupt, as described<br />
above. Next, you need to free-run the code (don’t use single stepping) so that the <strong>PIC24</strong>/<strong>dsPIC</strong> CPU can<br />
perform prioritization. You observe the order in which the breakpoints are hit. This procedure will become<br />
clearer after a few examples.<br />
Figure 8: Triggering the INT0 interrupt from the MPLAB debugger<br />
4.4.1 Interrupt Nesting Test<br />
The first interesting test is verifying that the preemptive scheduler QK_schedule_() is not called in a<br />
nested interrupt, but is called when the interrupt returns to the task level.<br />
To test this scenario, you place a breakpoint inside the _INT0Interrupt() and also inside the<br />
_T2Interrupt() ISR. When the breakpoint in the _T2Interrupt() is hit, you remove the original<br />
breakpoint and place another breakpoint at the very next machine instruction (use the Disassembly<br />
window) and also another breakpoint on the first instruction of the QK_schedule_() function. Next you<br />
trigger the INT0 interrupt per the instructions given in the previous section. You hit the Run button.<br />
The pass criteria of this test are as follows:<br />
1. The first breakpoint hit is the one inside the _INT0Interrupt() function, which means that the INT0<br />
ISR preempted the _T2Interrupt() ISR.<br />
2. The second breakpoint hit is the one in the _T2Interrupt(), which means that the Timer2 ISR<br />
continues after the _INT0Interrupt() completes.<br />
3. The last breakpoint hit is the one in QK_schedule_(), which means that the scheduler is called only<br />
after all interrupts are processed.<br />
You need to remove all breakpoints before proceeding to the next test.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
24 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
4.4.2 Task Preemption Test<br />
The next interesting test is verifying that tasks can preempt each other. You set a breakpoint anywhere in<br />
the Philosopher state machine code. You run the application until the breakpoint is hit. After this<br />
happens, you remove the original breakpoint and place another breakpoint at the very next machine<br />
instruction (use the Disassembly window). You also place a breakpoint inside the _INT0Interrupt()<br />
interrupt handler and on the first instruction at the first instruction of the QK_schedule_() function. Next<br />
you trigger the INT0 interrupt per the instructions given earlier. You hit the Run button.<br />
The pass criteria of this test are as follows:<br />
1. The first breakpoint hit is the one inside the _INT0Interrupt() function, which means that INT0 ISR<br />
preempted the Philospher task.<br />
2. The second breakpoint hit is the one in QK_schedule_() function, which means that the QK<br />
scheduler is activated before the control returns to the preempted Philosopher task.<br />
3. After hitting the breakpoint in QK_schedule_() function, you must first disable the Timer2 interrupts<br />
by clearing bit #7 in the IEC0 register at address 0x0094, in the “File Registers” window, in the similar<br />
way as you set the bit #0 in the IFS0 register to trigger INT0.<br />
4. Next, you single step into the QK_schedule_(). (NOTE: you might want to close the “File Registers”<br />
window to speed up the single stepping.) You verify that the scheduler invokes a state handler of the<br />
Table state machine. This proves that the Table task preempts the Philosopher task.<br />
5. After this you free-run the application and verify that the next breakpoint hit is the one inside the<br />
Philosopher state machine. This validates that the preempted task continues executing only after<br />
the preempting task (the Table state machine) completes.<br />
4.4.3 Other Tests<br />
Other interesting tests that you can perform include changing priority of the INT0 ISR to be lower than the<br />
priority of Timer2 ISR to verify that the QK scheduler is still called only after all interrupts complete.<br />
In yet another test you could post an event to Philosopher active object rather than Table active object<br />
from the INT0 ISR to verify that the QK scheduler will not preempt the Philosopher task by itself. Rather<br />
the extra event will be queued and the Philosopher task will process the queued event only after<br />
completing the current event processing.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
25 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
5 Implementing “Zero Interrupt Latency” with the NMI<br />
As noted in Section 3.2, the interrupt locking policy in this QP port locks only interrupts with IPL from 1 to<br />
6. The highest-level interrupts with IPL=7 are not locked at all. In other words, IPL=7-interrupts are nonmaskable<br />
(they are NMIs). This interrupt locking policy has two important consequences.<br />
First, the IPL=7-interrupts cannot call any QP services (e.g., NMIs cannot post events to QP tasks),<br />
because any system call from an NMI would run the risk of corrupting the internal QP data since the<br />
protection by critical sections does not apply for NMIs.<br />
On the flip side, however, the IPL=7-interrupts run completely “free” with the interrupt latency determined<br />
only by the underlying hardware and independent on the critical sections implemented in the QP. This is<br />
what is meant here by the term “Zero Interrupt Latency”.<br />
Such NMIs (IPL=7-interrupts, in this case) have many potential uses. They are very useful when the<br />
software must perform some simple operations very fast, but most of the time the software does not need<br />
to communicate with the task-level of QP active objects. Consider for example a fast interrupt-driven<br />
UART interface. The ISR that receives bytes must remove the bytes from the FIFO very fast or else the<br />
FIFO will quickly overflow. However, the ISR might buffer the bytes and only need to notify the task level<br />
when the buffer fills up, which is relatively infrequently.<br />
NOTE: NMIs (IPL=7-interrupts, in this case) can be equally well used in the non-preemptive QP port<br />
as well as in the preemptive port with the QK kernel.<br />
5.1 Communication between NMIs and QP<br />
From the previous discussion it is obvious that the NMIs must be able to communicate from time to time<br />
with the task level (active objects in QP). The question is how to achieve such communication without<br />
calling QP services directly<br />
The solution is to call QP services indirectly, by triggering a regular (maskable) interrupt from the NMI. In<br />
the <strong>PIC24</strong>/<strong>dsPIC</strong> architecture you can very easily trigger an ISR by explicitly setting the corresponding<br />
interrupt flag (the same that you explicitly clear in the ISR). The triggered interrupt can call QP services,<br />
such as posting or publishing events to QP active objects.<br />
The subtle, but important consideration is the communication between the NMI and the triggered<br />
maskable interrupt. Typically, such communication must occur by means of atomically-accessed shared<br />
variables. In the <strong>PIC24</strong>/<strong>dsPIC</strong> CISC architecture most 8- and 16-bit variables are read and written<br />
atomically (in one machine instruction), so implementing this way of communication is quite easy. In other<br />
CPUs, such as RISC load-store architectures, the implementation of atomic access is trickier and requires<br />
using special swap (SWP) instructions.<br />
5.2 Implementation Considerations for the NMIs<br />
Because the NMIs run completely “free” and outside the QP framework, they don’t need to follow any<br />
conventions associated with regular maskable ISRs that can call QP services. Generally, the NMIs should<br />
run as fast as possible, because the interrupt latency for NMIs is determined typically by the execution<br />
time of the NMI itself. (In this QP port, only one IPL level 7 is available for NMIs, so NMIs cannot preempt<br />
each other).<br />
NMIs can be coded in C, and in fact the <strong>C30</strong> compiler offers options to make these interrupts very<br />
efficient. On of these options is __shadow__, which uses fast context save and restore to the shadow<br />
registers. Obviously, you can use only one such fast ISR in your system to avoid corruption of the shadow<br />
registers. And finally, you should generally avoid calling any functions from the NMI, as this would force<br />
the compiler to save and restore w0-w7.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
26 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
6 BSP for the Explorer 16 Board<br />
The Board Support Package (BSP) for <strong>PIC24</strong>/<strong>dsPIC</strong> and Explorer 16 board (see Figure 1) with the nonpreemptive<br />
“vanilla” scheduler is located in the directory: \examples\pic24_dspic\vanilla\<br />
mplab-c30\dpp-qk-explorer16_pic24\ for <strong>PIC24</strong> and \examples\pic24_dspic\vanilla\<br />
mplab-c30\dpp-qk-explorer16_dspic\ for <strong>dsPIC</strong>. The BSP consists of the following files:<br />
1. bsp.h contains the Board Support Package interface (BSP)<br />
2. bsp.c contains the implementation of the BSP, which includes all ISRs and all platform-specific QP<br />
callbacks.<br />
3. p24FJ128GA010.gld contains the linker command file for locating the application.<br />
6.1 Setting the Sizes of Stack and Heap<br />
You set the sizes of stack and heap through the MPLAB IDE, as shown in Figure 9.<br />
Figure 9 Setting the size of heap and stack<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
27 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
6.2 The BSP header file bsp.h<br />
The BSP header file for the DPP application defines the desired ticking rate. This constant is useful for<br />
defining timeouts, which are always specified in units of clock ticks:<br />
#define BSP_TICKS_PER_SEC 100<br />
void BSP_init(void);<br />
void BSP_displyPhilStat(uint8_t n, char const *stat);<br />
void BSP_busyDelay(void); /* to artificially extend RTC processing */<br />
6.3 BSP initialization<br />
The following BSP_init() function for the Explorer 16 board configures the DSP and peripherals as,<br />
shown in Listing 11. This file has been designed to be easily modifiable for other applications.<br />
Listing 11: The BSP_init() implementation in the bsp.c for the DPP example.<br />
void BSP_init(void) {<br />
RCONbits.SWDTEN = 0; /* disable Watchdog */<br />
TRISA = 0x00; /* set LED pins as outputs */<br />
PORTA = 0x00; /* set LEDs drive state low */<br />
}<br />
if (QS_INIT((void *)0) == 0) { /* initialize the QS software tracing */<br />
Q_ERROR();<br />
}<br />
6.4 Starting Interrupts in QF_onStartup()<br />
QP invokes the QF_onStartup() callback just before starting the background loop. The<br />
QF_onStartup() function must configure and start interrupts. At the minimum, QF_onStartup() must<br />
start the system clock tick interrupt. The following QF_onStartup() function enables the CPU Timer 0.<br />
void QF_onStartup(void) { /* entered with interrupts locked */<br />
T2CON = 0; /* Use Internal Osc (Fcy), 16 bit mode, prescaler = 1 */<br />
TMR2 = 0; /* Start counting from 0 and clear the prescaler count */<br />
PR2 = BSP_TMR2_PERIOD - 1; /* set the timer period */<br />
_T2IP = TIMER2_ISR_PRIO; /* set Timer 2 interrupt priority */<br />
_T2IF = 0; /* clear the interrupt for Timer 2 */<br />
_T2IE = 1; /* enable interrupt for Timer 2 */<br />
T2CONbits.TON = 1; /* start Timer 2 */<br />
}<br />
6.5 Assertion Handling Policy in Q_onAssert()<br />
As described in Chapter 6 of [PSiCC2], all QP components use internally assertions to detect errors in the<br />
way application is using the QP services. You need to define how the application reacts in case of<br />
assertion failure by providing the callback function Q_onAssert(). Typically, you would put the system in<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
28 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
a fail-safe state and try to reset. It is also a good idea to log some information as to where the assertion<br />
failed.<br />
The following code fragment shows the Q_onAssert() callback for <strong>PIC24</strong>/<strong>dsPIC</strong>. The function keeps<br />
locking interrupts in an endless loop. You can then break into the code with the debugger and inspect the<br />
cause of the assertion by backtracking the call stack.<br />
NOTE: This policy is only adequate for testing, but is not adequate for production release.<br />
void Q_onAssert(char const Q_ROM * const Q_ROM_VAR file, int line) {<br />
(void)file; /* avoid compiler warning */<br />
(void)line; /* avoid compiler warning */<br />
for (;;) {<br />
QF_INT_LOCK(dummy); /* make sure that interrupts are locked */<br />
}<br />
}<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
29 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
7 The QS Software Tracing Instrumentation<br />
QS is a software tracing facility built into all QP components and also available to the Application code.<br />
QS allows you to gain unprecedented visibility into your application by selectively logging al-most all<br />
interesting events occurring within state machines, the framework, the kernel, and your application code.<br />
QS software tracing is minimally intrusive, offers precise time-stamping, sophisticated runtime filtering of<br />
events, and good data compression (see Chapter 11 in PSiCC2 [PSiCC2]).<br />
This <strong>QDK</strong> demonstrates how to use the QS software tracing instrumentation to generate real-time trace of<br />
a running QP application. QS can be configured to send the trace data out of the serial port of the target<br />
device. On the Explorer 16 board, QS uses the built-in UART2 to send the trace data to the host. The QS<br />
platform-dependent implementation is located in the file bsp.c and looks as follows:<br />
(1) #ifdef Q_SPY<br />
Listing 12: QS implementation to send data with UART2 of <strong>PIC24</strong>/<strong>dsPIC</strong> MCU<br />
(2) static uint32_t l_tickTime; /* timestamp at tick */<br />
(3) enum AppRecords { /* application-specific trace records */<br />
PHILO_STAT = QS_USER<br />
};<br />
#define QS_BUF_SIZE<br />
#define BAUD_RATE<br />
1024U<br />
38400U<br />
(4) uint8_t QS_onStartup(void const *arg) {<br />
(5) static uint8_t qsBuf[QS_BUF_SIZE]; /* buffer for <strong>Quantum</strong> Spy */<br />
(6) QS_initBuf(qsBuf, sizeof(qsBuf)); /* initialize the QS trace buffer */<br />
/* initialize the UART2 for transmitting the QS trace data */<br />
(7) TRISFbits.TRISF5 = 0; /* set UART2 TX pin as output */<br />
U2MODE = 0x0008; /* enable high baud rate */<br />
U2STA = 0x0000; /* use default settings of 8N1 */<br />
U2BRG = ((BSP_FCY_HZ / 4 / BAUD_RATE) - 1); /* baud rate generator */<br />
U2MODEbits.UARTEN = 1;<br />
U2STAbits.UTXEN = 1;<br />
(8) QS_FILTER_ON(QS_ALL_RECORDS);<br />
/* setup the QS filters... */<br />
// QS_FILTER_OFF(QS_QEP_STATE_EMPTY);<br />
// QS_FILTER_OFF(QS_QEP_STATE_ENTRY);<br />
// QS_FILTER_OFF(QS_QEP_STATE_EXIT);<br />
// QS_FILTER_OFF(QS_QEP_STATE_INIT);<br />
// QS_FILTER_OFF(QS_QEP_INIT_TRAN);<br />
// QS_FILTER_OFF(QS_QEP_INTERN_TRAN);<br />
// QS_FILTER_OFF(QS_QEP_TRAN);<br />
// QS_FILTER_OFF(QS_QEP_dummyD);<br />
QS_FILTER_OFF(QS_QF_ACTIVE_ADD);<br />
. . .<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
30 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
return (uint8_t)1; /* indicate successfull QS initialization */<br />
}<br />
/*..........................................................................*/<br />
(9) void QS_onCleanup(void) {<br />
}<br />
/*..........................................................................*/<br />
(10) void QS_onFlush(void) {<br />
uint16_t b;<br />
while ((b = QS_getByte()) != QS_EOD) { /* next QS trace byte available */<br />
while (U2STAbits.UTXBF != 0U) { /* TX Buffer full */<br />
}<br />
U2TXREG = (uint8_t)b; /* stick the byte to TXREG for transmission */<br />
}<br />
}<br />
/*..........................................................................*/<br />
/* NOTE: invoked within a critical section (inetrrupts disabled) */<br />
(11) QSTimeCtr QS_onGetTime(void) {<br />
(12) if (_T2IF == 0) {<br />
(13) return l_tickTime + (uint32_t)TMR2;<br />
}<br />
else {<br />
(14) return l_tickTime + BSP_TMR2_PERIOD + (uint32_t)TMR2;<br />
}<br />
}<br />
#endif /* Q_SPY */<br />
(1) The QS instrumentation is enabled only when the macro Q_SPY is defined<br />
(2) The l_tickTime variable is used to hold the 32-bit-wide the timestamp at tick and is updated in the<br />
system clock tick ISR.<br />
(3) This enumeration defines application-specific QS trace record(s), to demonstrate how to use them.<br />
(4) You need to define the QS_init() callback to initialize the QS software tracing.<br />
(5) You should adjust the QS buffer size (in bytes) to your particular application<br />
(6) You always need to call QS_initBuf() from QS_init() to initialize the trace buffer.<br />
(7) This block of code sets up UART2 to output data at the desired baud rate and standard format<br />
(8N1).<br />
(8) The QS filters are set-up statically.<br />
(9) The QS_onCleanup() callback performs the cleanup of QS. Here nothing needs to be done.<br />
(10) The QS_onFlush() callback flushes the QS trace buffer to the host. Typically, the function busywaits<br />
for the transfer to complete. It is only used in the initialization phase for sending the QS<br />
dictionary records to the host (see please refer to “QSP Reference Manual” section in the “QP/C<br />
Reference Manual” an also to Chapter 11 in [PSiCC2])<br />
7.1 QS Time Stamp Callback QS_onGetTime()<br />
The platform-specific QS port must provide function QS_onGetTime() (Listing 12(11)) that returns the<br />
current time stamp in 32-bit resolution. To provide such a fine-granularity time stamp, the <strong>PIC24</strong>/<strong>dsPIC</strong><br />
port uses the Timer2, which is the same timer already used for generation of the system clock-tick<br />
interrupt.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
31 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
NOTE: The QS_onGetTime() callback is always called with interrupts locked.<br />
Figure 10 shows how the Timer2 is extended to 32 bits. The TMR2 register counts up from zero to the<br />
reload value stored in the PR2 register. When TMR2 reaches PR2, the hardware automatically clears TMR2<br />
on the subsequent clock tick. Simultaneously, the hardware sets the _T2IF flag, which triggers the<br />
system clock tick interrupt.<br />
The system clock tick ISR _T2Interrupt() keeps updating the “tick count” variable l_tickTime (see<br />
Listing 4(5) and Listing 8(2)) by incrementing it each time by QS_tickPeriod_. The clock-tick ISR also<br />
clears the _T2IF flag.<br />
Figure 10: Using 16-bit Timer2 to provide 32-bit QS time stamp.<br />
count<br />
32-bit time stamp<br />
returned from<br />
QS_onGetTime()<br />
count in<br />
_T2Interrupt()<br />
System<br />
clock tick<br />
period<br />
0x00FFFFFF<br />
Count in TMR2<br />
Current Register<br />
Timer2<br />
Reload<br />
Value<br />
Register<br />
System<br />
clock-tick<br />
time<br />
Listing 12(11-15) shows the implementation of the function QS_onGetTime(), which combines all this<br />
information to produce a monotonic time stamp.<br />
(12) The QS_onGetTime() function tests the _T2IF flag to determine whether This flag being set means<br />
that the TMR2 counter has rolled over to zero, but the _T2Interrupt() ISR has not run yet<br />
(because interrupts are still locked).<br />
(13) Most of the time the _T2IF flag is not set, and the time stamp is simply the sum of l_tickTime +<br />
(uint32_t)TMR2).<br />
(13) If the _T2IF flag is set, the TMR2 counter misses one update period and must be additionally<br />
incremented by BSP_TMR2_PERIOD_, which is a compile-time constant.<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
32 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
7.2 QS Trace Output in QF_onIdle()/QK_onIdle()<br />
To be minimally intrusive, the actual output of the QS trace data happens when the system has nothing<br />
else to do, that is, during the idle processing. The following code snippet shows the code placed either in<br />
the QF_onIdle() callback (“Vanilla” port), or QK_onIdle() callback (in the QK port):<br />
Listing 13: QS trace output using the UART0 of the Stellaris LM3S811 MCU<br />
#define UART_TXFIFO_DEPTH 16<br />
. . .<br />
void QK_onIdle(void) {<br />
. . .<br />
#ifdef Q_SPY<br />
(1) while (U2STAbits.UTXBF == 0U) { /* TX Buffer not full */<br />
uint16_t b;<br />
(2) QF_INT_LOCK(dummy);<br />
(3) b = QS_getByte();<br />
(4) QF_INT_UNLOCK(dummy);<br />
(5) if (b == QS_EOD) { /* End-Of-Data reached */<br />
(6) break; /* break out of the loop */<br />
}<br />
(7) U2TXREG = (uint8_t)b; /* stick the byte to TXREG for transmission */<br />
}<br />
#elif defined NDEBUG<br />
__asm__ volatile("disi #0x0001");<br />
Idle(); /* transition to Idle mode, see NOTE03 */<br />
#else<br />
QF_INT_UNLOCK(dummy); /* unlock interrupts, see NOTE01 */<br />
#endif<br />
}<br />
(1) As long as the UTXBF (TX buffer full) flag is not set, the UART2 TX FIFO can accept new bytes.<br />
(2) Interrupts are locked to call QS_getByte().<br />
(3) The function QS_getByte() returns the next byte or QS_EOD (end-of-data) if the QS ring buffer is<br />
empty.<br />
(4) The interrupts are unlocked after the call to QS_getByte().<br />
(5-6) If end-of-data is reached the loop is terminated for this invocation of QK_onIdle().<br />
(7) The byte obtained from the QS buffer is inserted into the UART2 transmit register for transmission.<br />
7.3 Invoking the QSpy Host Application<br />
The QSPY host application receives the QS trace data, parses it and displays on the host workstation<br />
(currently Windows or Linux). For the configuration options chosen in this port, you invoke the QSPY host<br />
application as follows (please refer to “QSP Reference Manual” section in the “QP/C Reference Manual”<br />
an also to Chapter 11 in [PSiCC2]):<br />
qspy –cCOM1 –b38400 –O2 –F2 –S1 –E1 –Q1 –P1 –B1<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
33 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
8 Related Documents and References<br />
Document<br />
[PSiCC2] “Practical UML Statecharts in C/C++,<br />
Second Edition”, Miro Samek, Newnes, 2008<br />
[QP 08] “QP Reference Manual”, <strong>Quantum</strong><br />
<strong>Leaps</strong>, LLC, 2008<br />
[QL AN-Directory 07] “Application Note: QP<br />
Directory Structure”, <strong>Quantum</strong> <strong>Leaps</strong>, LLC, 2007<br />
[QL AN-DPP 08] “Application Note: Dining<br />
Philosopher Problem Application”, <strong>Quantum</strong><br />
<strong>Leaps</strong>, LLC, 2008<br />
[MPLAB-<strong>C30</strong>] “MPLAB® C Compiler for <strong>PIC24</strong><br />
MCUs and <strong>dsPIC</strong>® DSCs User’s Guide”,<br />
Microchip, 2008.<br />
[<strong>PIC24</strong>] “<strong>PIC24</strong>FJ128GA010 Family Data Sheet<br />
64/80/100-Pin General Purpose, 16-Bit Flash<br />
Microcontrollers”, Microchip, 2007.<br />
[<strong>dsPIC</strong>] “<strong>dsPIC</strong>30F/33F Programmer’s Reference<br />
Manual High-Performance Digital Signal<br />
Controllers”, Microchip, 2005.<br />
[QKernel] “Q.Kernel for <strong>PIC24</strong> and <strong>dsPIC</strong> User<br />
Guide Version 2.1-1089”, Quasarsoft Ltd, 2009<br />
[AVIX] AVIX RTOS for Microchip 16-bit and 32-<br />
bit micro controllers belonging to the <strong>PIC24</strong>F,<br />
<strong>PIC24</strong>H, <strong>dsPIC</strong>30F, <strong>dsPIC</strong>33F and PIC32<br />
families.<br />
[Samek 07a] “Using Low-Power Modes in<br />
Foreground/Background Systems”, Miro Samek,<br />
Embedded System Design, October 2007<br />
http://www.state-machine.com/doxygen/qpn/<br />
Location<br />
Available from most online book retailers, such as<br />
amazon.com. See also: http://www.statemachine.com/psicc2.htm<br />
http://www.statemachine.com/doc/AN_QP_Directory_Structure.pdf<br />
http://www.state-machine.com/doc/AN_DPP.pdf<br />
Microchip literature #DS51284H, available on the<br />
Explorer 16 kit and online at www.microchip.com<br />
Microchip literature #DS39747D, available on the<br />
Explorer 16 kit and online at www.microchip.com<br />
Microchip literature #DS70157B, available on the<br />
Explorer 16 kit and online at www.microchip.com<br />
Document available for download from<br />
www.quasarsoft.com<br />
http://www.avix-rt.com/<br />
http://www.embedded.com/design/202103425<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
34 of 35
<strong>QDK</strong><br />
<strong>PIC24</strong>/<strong>dsPIC</strong>-<strong>C30</strong><br />
www.state-machine.com/pic<br />
9 Contact Information<br />
<strong>Quantum</strong> <strong>Leaps</strong>, LLC<br />
103 Cobble Ridge Drive<br />
Chapel Hill, NC 27516<br />
USA<br />
+1 866 450 LEAP (toll free, USA only)<br />
+1 919 869-2998 (FAX)<br />
e-mail: info@quantum-leaps.com<br />
WEB : http://www.quantum-leaps.com<br />
http://www.state-machine.com<br />
“Practical UML<br />
Statecharts in C/C+<br />
+, Second Edition:<br />
Event Driven<br />
Programming for<br />
Embedded<br />
Systems”,<br />
by Miro Samek,<br />
Newnes, 2008<br />
Microchip, Inc.<br />
Web: http://www.microchip.com<br />
Copyright © <strong>Quantum</strong> <strong>Leaps</strong>, LLC. All Rights Reserved.<br />
35 of 35