30.12.2014 Views

QDK PIC24/dsPIC-C30 - Quantum Leaps

QDK PIC24/dsPIC-C30 - Quantum Leaps

QDK PIC24/dsPIC-C30 - Quantum Leaps

SHOW MORE
SHOW LESS

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

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!