Most recent edit: 26-September-2009
Initial Draft:
6-May-2009
Nadler & Associates developed a product which adds USB functionality to an earlier product, using Microchip's PIC32MX USB-equipped microcontroller. The code for this add-on includes many thousands of lines of C++ previously running on a PC. Unfortunately, Microchip-provided tools do not support C++, so we used CodeSourcery's G++ v4.3-80 GNU-based toolchain.
CodeSourcery's G++ for MIPS ELF targets the Malta 24k. The package includes little-endian code generation and software floating point as required for PIC32MX. G++ also includes the complete C++ library (formerly known as the C++ standard library), which is missing from a number of commercial offerings claiming “C++ support”. Exceptions, collections, and so forth can be used with this package (carefully, PIC32MX only has 32kb of RAM).
To use CodeSourcery's MIPS ELF G++ on PIC32MX required the adaptations described below. A minimal example program (just blinks the LEDs) for the Microchip USB Starter Board contains these adaptations and is available here: 20090926_CPP_demo.zip. See ReadMe.txt in the ZIP for instructions on loading and running this example.
Microchip's chip programming and debugging tools use proprietary interfaces, and are consequently not supported by 3rd party tools. We used the PIC32MX JTAG port for programming via the low-cost USBJTAG-NT JTAG adapter.
On startup out of reset, PIC32MX sets many of the hardware control parameters from a set of reserved locations in flash. For example, this is required to start the chip in a configuration that matches the hardware-provided clock. Microchip added a proprietary set of #pragma config's to their C compiler, but these are not supported by any other toolchain, and make it easy to scatter initialization information around the code (and hard to find the actual configuration used). We replaced the Microchip #pragma config's with a simple initialized array loaded into the reserved locations.
We created startup code that does the minimal
required low-level initialization run by the chip when it is reset.
This startup code puts the PIC32MX into vectored interrupt mode,
performs some very basic hardware initialization, saves the
reason-code for the the reset, then calls the CodeSourcery “CS3”
initialization layer. CS3 brings up the C/C++ environment
(initializing memory, calling constructors for static objects, etc),
then calls the application main().
See PIC32MX-reset.S
Microchip added additional attributes for
declaring an ISR, neither supported by CodeSourcery nor generally by GCC.
To replace these Microchip-proprietary attributes,
we created a simple assembly wrapper that calls a C-language ISR.
The wrapper creates both the MIPS interrupt vector branch entry and the
ISR prologue/epilogue (saves/restores the processor state).
SeeISRwrapper.inc and the
example invocation in ISRwrapper.S
ISR routines in Microchip-provided libraries will
compile as coded, but the unsupported attributes will be ignored. For
each required ISR, add an entry in ISRwrapper.S
For
example, for the USB ISR declaration coded as:
#pragma interrupt _USB1Interrupt
ipl4 vector 45
void _USB1Interrupt( void )
add a line to ISRwrapper.S:
ISR_wrapper 45,_USB1Interrupt # USB vector 45 routes to C-language ISR “_USB1Interrupt”
Directed by the linker script, the linker places
code and data in specific memory regions available in PIC32MX,
creates a copy of initialized data in ROM for use during
initialization, fills in the interrupt vectors, allocates stack and
heap (and checks for adequate space), etc. It does not translate
“logical” or “virtual” memory addresses to
the “physical” addresses required for programming the
PIC32MX; this must be performed by the device programmer (or some
other post-processing of the linker output).
See
csGCC_PIC32MX_flash.ld
CodeSourcery G++ is based on the newlib version of the C libraries. In addition to the standard C library (memory management for example), newlib provides a simple method of filling in primitives normally provided by an OS.
Implementing read() and write() redirects IO; in the example program
stdin/stdout/stderr are redirected to UART1. Similarly file IO can be
redirected to a non-OS implementation (for example a file system
implemented on top of Microchip's USB stack).
See
DRN_PIC32MX_CS_glue.c
The example program includes an ISR-based implementation of
character IO to UART1, including work-arounds for a couple of
PIC32MX UART hardware 'features'.
See
DRN_PIC32MX_UART1.h/.c
Implementing time() provides clock information to the runtime
library. The example includes an implementation of time() using
a PIC32MX timer.
See
PIC32MX_time.h/.cpp
The Microchip 1.04 compiler distribution includes header files for PIC32MX hardware and libraries, but unfortunately some of these files are co-mingled with the base C-language headers. A different toolchain with differing C-language headers (such as any later GCC) will conflict, and compilation will fail. It is necessary to segregate the PIC32MX headers from the base C language headers for succesful compilation. Also, some of the headers are not valid C++ and minor corrections are required; details can be found here: work-arounds for Microchip tool issues. Contact Dave Nadler if a copy of the segregated and C++-compatible headers is required.
We implemented an example program which demonstrates basic file operations with a USB memory stick, using UART1 as the console. As currently implemented it requires N&A hardware and does not run on Microchip evaluation boards. Contact Dave Nadler if this is required.
Not yet implemented are:
exception handler (to politely catch things like dereferencing an illegal pointer)
bootstrap call-out and bootstrap exception-vector
Copyright © 2009 - Dave Nadler - All Rights Reserved
Contact Dave Nadler:
USA East Coast voice (978) 263-0097
Skype Dave.Nadler1
Dave.Nadler@Nadler.com