Overview

To produce an executable for a given target from a set of Java source files and libraries 3 sets of tools are used:

  1. Java tools. E.g. a java compiler from Oracle, or the Jikes compiler from IBM. Any SDK can be used such as e.g. Oracle, OpenJDK or GNU Classpath. Any editor will do as well, but an Eclipse plugin is supplied with the HVM to ease development and deployment. No matter what is the preferred development environment the outcome is a set of class files making up the application.

  2. HVM tools. These tools make up the core part of the HVM environment. The user points to the main entry point of the application (the main method) and the tools will now do two things:
    1. Compute the dependency extent of the main method. This is the set of methods that may be executed as a result of running main, and the set of fields and other resources (e.g constants) that may be referenced. The tool will compute a conservative estimate of this set through a static analysis of the source code. The dependency extent will contain both code produced by the user and code extracted from referred libraries.
    2. Translate the dependency extent into C code. The C code produced is basically a set of unsigned char arrays containing the byte code and a C representation of the constant pools. This C code can be thought of as a C version of the class files making up the dependency extent of the main method.
    Thus the outcome of the HVM tools is a set of auto generated C files making up the C version of the application.

  3. C tools . These tools are the existing set of tools used by the embedded developer to translate C source code into downloadable hex files for the target. In many cases this will be some version of the GCC tool chain, but it may also be some other C tool chain like e.g. the IAR C tool chain from IO Systems. Using this C compiler the C source generated by the HVM tools can be linked with the HVM interpreter to produce the final executable for the target.

 

Under the download section below the HVM tools and the HVM interpreter can be downloaded. The HVM tools are distributed as an Eclipse plugin. The HVM interpreter is distributed as a flat set of very few ANSI C "no funny stuff" C source files without any dependencies to particular environments or libraries. The HVM interpreter is embedded into the plugin and will be installed automatically by the plugin.

 

The tutorial describes in detail how to download and install the HVM tools. The programming style applied will be to first use Eclipse to edit the Java program source, then activate the plugin to translate the Java source into C, and finally use an existing environment for translating C into a final executable for the target. For a specific platform it will be possible to extend the HVM tools to perform the C compilation and download from inside Eclipse without involving the user, but this has not been done for any specific target yet.

 

The tutorial describes how to use the HVM for three different targets.

 

Dependency leaks

Given the limited size of the target platforms (approx. 8kB RAM and approx 256 Kb ROM), it is obvious that the dependency extent of a given Java application cannot be too great. Sometimes adding a simple code segment and rerunning the HVM tools, the size of the dependency extent may explode. This may manifest itself in two ways:

 

  1. The Eclipse job calculating the dependency extent runs for a long time, or may even fail with a StackOverflowError.
  2. The job may eventually finish but the size of the auto generated source code becomes impractical and will no longer fit on the device.

 

If this happens a dependency leak has been introduced. E.g. using 'System.out.println' will introduce a dependency leak. Another example is the 'java.util.Random' class - instantiating this class will also cause a dependency leak. On the other hand 'java.util.ArrayList' can be used and does not leak. Predicting which parts of the SDK can be used is difficult without detailed knowledge about their implementation, so trial and error is a good approach. Run the analyzer frequently to discover leaks as they are being introduced.

 

Native methods

Using Hardware Objects and 1st level interrupt handling - both features supported by the HVM - it is no longer necessary to use native methods. But if existing SDKs are used the dependency extent may contain various native methods. These will be declared as external in the auto generated C files and must be implemented by the user if they are important for proper operation of the program. All native methods are supplied a pointer to the Java stack (sp). They must leave their return value on sp[0] if the native method returns anything, and they can read their parameters (if any) from sp[0] and onwards. The native method should return -1 if successful. If the linking of the auto generated C files with the interpreter suddenly causes a linking error, it may be because some native methods have been included in the dependency extent. In this case the native method must be implemented and linked with the application.

 

Linking

The process of linking the C files generated by the HVM tools plugin with the HVM interpreter itself and any native methods (if any) is not described here. This is because the C tools and C programming environment used by the user will differ a great deal from target to target, and it is assumed that the user is able to adapt the existing C build environment to include the C files generated be the HVM tools. To ease integration with the existing C development environment all files generated by the HVM tools and the interpreter itself are written in strict, old school, no funny stuff, ANSI C. Additionally the code is completely self contained and does not rely on any library or profile.

Porting

This section describes the general steps required to port the HVM to a new platform. The tutorial linked to at the top of the page contains more information about how these steps have been carried out on a selection of architectures.

 

Machine types

The file types.h defines the sizes of the basic integer data types. For each new architecture a new section must be added. E.g. for the Lego NXT (SAM7S256 micro controller) the following section has been added:

...
#if defined(SAM7S256)
#undef PACKED
#define PACKED
#define DATAMEM __attribute__ ((section (".data")))
#define PROGMEM
#define RANGE
#define pgm_read_byte(x) *((unsigned char*)x)
#define pgm_read_word(x) *((unsigned short*)x)
#define pgm_read_pointer(x, typeofx) *((typeofx)x)
typedef int int32;
typedef unsigned int uint32;
typedef unsigned int pointer;
#else
...

If you add a similar section for your target, you are invited to send it to the author so that it may be included in the official distribution.

 

HVM Defines

The two attributes JAVA_HEAP_SIZE and JAVA_STACK_SIZE must be defined when building the HVM. E.g. reasonable values for the Lego NXT was -DJAVA_HEAP_SIZE=8192 -DJAVA_STACK_SIZE=512. The heap size is in bytes whereas the stack size is in 32 bit words, ie. the define above will cause the HVM to allocate 2048 bytes for the main Java stack.
If the exsisting C based platform has its own main function (as it most likely will) add the following define as well -DEXCLUDEMAIN.

 

Native functions

The following native functions must be implemented for the target:

These functions have already been implemented for several architectures in the files natives_*.c. A new port must either include one of these in the build or make a new one specific for the target in question.
 

Update your existing build environment

After the above changes add the HVM interpreter, support and autogenerated C files containing the translated Java code to an exisiting C based build environment for the target. The minimal selection of files that must be added is:

icecapvm.c, natives_allOS.c, methodinterpreter.c, allocation_point.c, methods.c, classes.c, gc.c, print.c

Only methods.c and classes.c will change when the Java source is changed. The remaining files contain the interpreter and various support and will stay unchanged. The icecaptools plugin will automatically place these files in the output folder specified by the user.

 

Build your target API

Using hardware objects it is possible to control target peripherals such as buttons, LEDs and displays from inside Java. Usually such facilities will be available already implemented in C, and it is easy to create native methods to call such C functionality from inside Java as an alternative to reimplementing low level device control in Java. The HVM supports preemptive threading and 1st level interrupt handling as well. How to enable that for a new target is not descriped here.