Author: | Dean Hall |
---|---|
Id: | HowToPortPyMite.txt 599 2010-09-14 18:34:20Z dwhall256 |
This document describes in a step-by-step manner how to port the PyMite interpreter to a new platform. In doing so, it serves as a guide for the PyMite developer.
The following is needed in order to port and compile PyMite to a new platform:
Starting from a sample project that configures the the communications peripheral serving as stdio (UART) and has a working build system (Makefile) is highly recommended.
Here is a step-by-step guide to porting PyMite to a new platform.
Copy an existing platform directory that most closely resembles the new target platform. For the rest of this document, the new directory is referred to as <plat>. The core files needed for the port are main.c, plat.c, plat.h, pmfeatures.py and Makefile. Other files that might be useful are `` main.py`` and SConscript. Remove all the extraneous files.
Edit pmfeatures.py to set the features that you want the VM to have. Find the meaning of all the HAVE_* variables in src/vm/pmFeatureDependencies.h.
Edit plat.h to set the size of the platform's heap in PM_HEAP_SIZE and be sure to leave enough RAM for the C stack.
Edit the build environment so that it builds the PyMite library, ../../vm/libpmvm_<plat>.a, and links it to main.c, plat.c and the native and image files that are generated by pmImgCreator.py from your Python source code. Below are example lines for a Makefile that would be needed in addition to make a complete Makefile:
# PyMite: Configuration PLATFORM := $(notdir $(CURDIR)) PM_LIB_ROOT = pmvm_$(PLATFORM) PM_LIB_FN = lib$(PM_LIB_ROOT).a PM_LIB_PATH = ../../vm/$(PM_LIB_FN) PM_USR_SOURCES = main.py PMIMGCREATOR := ../../tools/pmImgCreator.py PMGENPMFEATURES := ../../tools/pmGenPmFeatures.py TARGET = main SRC = $(TARGET).c plat.c $(TARGET)_nat.c $(TARGET)_img.c # If you want to be able to run ipm, uncomment the following line #IPM = true # PyMite: CFLAGS ifeq ($(DEBUG),true) CDEFS += -g -gstabs -D__DEBUG__=1 endif CINCS += -I$(abspath .) CFLAGS += $(CDEFS) $(CINCS) # Define the toolchain CC = arm-elf-gcc CPP = arm-elf-g++ OBJCOPY = arm-elf-objcopy OBJDUMP = arm-elf-objdump SIZE = arm-elf-size AR = arm-elf-ar NM = arm-elf-nm # PyMite: Build the VM archive if it doesn't exist pmvm : $(PM_LIB_PATH) $(PM_LIB_PATH) : make -C ../../vm pmfeatures.h : pmfeatures.py $(PMGENPMFEATURES) $(PMGENPMFEATURES) pmfeatures.py > $@ # PyMite: Generate native code and module images from the python source $(TARGET)_nat.c $(TARGET)_img.c : $(PM_USR_SOURCES) $(PMIMGCREATOR) -f pmfeatures.py -c -u -o $(TARGET)_img.c --native-file=$(TARGET)_nat.c $(PM_USR_SOURCES) # PyMite: tell the compiler where to find pm.h and libpmvm_<plat>.a CFLAGS += -I../../ LDFLAGS += -L../../vm -lpmvm_<plat> # PyMite: Append pmvm to the list of .PHONY targets .PHONY : pmvm # PyMite: Export these env variables so the Makefile in ../../vm gets them export CC OBJCOPY NM CFLAGS ALL_CFLAGS AR IPM PM_LIB_FN # PyMite: Append these intermediate files to the list of things to clean clean : $(REMOVE) $(TARGET)_img.c $(REMOVE) $(TARGET)_nat.c $(REMOVE) pmfeatures.py
Edit plat.c to replace #include statements with ones for the new platform. Edit the plat_init() function to enable the peripheral to be used for PyMite's stdio (usually UART0). If PyMite sys.time() or threading is desired, also edit plat_init() to initialize a timer or other periodic callback and the corresponding timer interrupt service routine (ISR) or callback function. The ISR or callback function simply needs to call pm_vmPeriodic(<us>); with an argument that is the number of microseconds that have passed since it was last called.
Edit the plat_memGetByte() function in plat.c to match the platform's memory architecture. A microcontroller that uses the von Neumann architecture, such as ARM7TDMI, the RAM and program memory will use the same kind of memory access. Here is an implementation for von Neumann architectures:
uint8_t plat_memGetByte(PmMemSpace_t memspace, uint8_t const **paddr) { uint8_t b = 0; switch (memspace) { case MEMSPACE_RAM: case MEMSPACE_PROG: b = **paddr; *paddr += 1; return b; /* Removed unused cases for brevity */ } }
However, a microcontroller that uses the Harvard architecture, such as AVR, might need an API call to read from the program memory. Here is an implementation for Harvard architecture with AVR-specific API:
uint8_t plat_memGetByte(PmMemSpace_t memspace, uint8_t const **paddr) { uint8_t b = 0; switch (memspace) { case MEMSPACE_RAM: b = **paddr; *paddr += 1; return b; case MEMSPACE_PROG: b = pgm_read_byte(*paddr); *paddr += 1; return b; /* Unused cases removed for brevity */ } }
Edit the plat_getByte() function in plat.c to read a byte from the platform's designated stdio peripheral and return the byte by reference. Follow the existing code to raise an exception if an I/O error occurs.
Edit the plat_putByte() function in plat.c to write a byte to the platform's designated stdio peripheral. Follow the existing code to raise an exception if an I/O error occurs.
Edit the plat_getMsTicks() function in plat.c to return, by reference, the current number of milliseconds since system power-up (or reset if that is preferred). This function may either return pm_timerMsTicks which requires that pm_vmPeriodic() be called faithfully, or it may fetch a value from the platform's time-keeping system (such as a real time clock peripheral). The datatype holding the number of milliseconds is four bytes (32 bits). This means that it might be required to disable interrupts while copying the value in order to avoid corruption; furthermore, PyMite will experience system time "rollover" every 49 days (roughly).
Edit the plat_reportError() function in plat.c to report unhandled exceptions and other incorrect return states. A common method is to print on stdout. The desktop and avr platform implementations demonstrate how to print the error status and a Python-like traceback on stdout.