by Madhu Siddalingaiah

Object Expo: Embedded Java systems

news
May 1, 19998 mins

New Java technologies address embedded systems' needs

Embedded systems are everywhere: in cars, consumer electronics, computer peripherals — in just about anything electronic. In the past, the software for these systems was written by professionals with highly specialized skills. A mixture of assembly and high-level languages such as C were often used. The combination was functional, but a wayward pointer resulted in system failure. While users may accept bugs in desktop applications, they will not tolerate the possibility of rebooting their toaster. Moreover, in many cases embedded processors are used in mission-critical applications such as medical instrumentation or security systems. A failure here could yield catastrophic results.

Developers of desktop applications, server middleware, and applets have shown considerable interest in Java. Cross-platform neutrality, object orientation, a robust security model, and an extensive set of class libraries are often cited as reasons for Java’s success. Missing from the types of platforms using Java is the one platform Java was designed for: embedded systems.

Java has not made substantial inroads into embedded systems because of the lack of infrastructure. At the time of this writing, there are few embedded platforms on which to run Java. Fortunately, this situation is quickly changing.

The three Java specifications for embedded devices

Currently there are three subset Java specifications designed for use in the embedded space:

  • PersonalJava, or “PJava,” is meant to be used for fixed-function devices with a graphic display and user interface.

  • Embedded Java, based on JDK 1.1, is targeted at nongraphic devices. It permits subsetting of the class libraries as necessary.

  • Java Card is specifically designed for smart cards with very tight memory and processor constraints. Java Card does not support data types larger than 16 bits or multidimensional arrays. Because there are numerous applications where 8/16 bit processors are ideal but 32-bit processors are too costly, the Java Card specification might be applicable in these situations.

These specifications illustrate the diversity of embedded systems. The availability of system resources like memory and processing power varies dramatically.

Interpret Java bytecode

Software implementation of the Java virtual machine (JVM) requires some means of interpretation or translation of Java bytecodes into native code. To accomplish this, several well-known techniques exist for executing Java bytecodes. They include interpreters, just-in-time (JIT) translators, and static compilers, each of which can be used for embedded devices depending upon the circumstances.

Interpreters are straightforward. They simply simulate a Java machine at runtime by loading each Java opcode and performing the appropriate operation. This simplicity exacts a cost: performance. Java interpreters tend to be slow, often an order of magnitude slower than compiled C/C++ code.

To solve performance problems, JIT translators have been implemented with good success. JIT translators accept Java bytecodes and translate them to native code just prior to execution. JIT translators are effective and solve the most challenging problem for VM designers: dynamic class loading.

The problem stems from a Java program that may load an arbitrary class file by calling Class.forName(), which is loosely analogous to loading a shared object or a dynamically linked library (DLL). Dynamic class loading requires significant program and data memory, which drives up size and cost, an outcome best avoided if possible.

Depending on the application, however, dynamic class loading via Class.forName() may not be necessary. The vast majority of today’s embedded systems do not load code dynamically and therefore do not require dynamic class loading. For these applications, statically compiled code may be sufficient.

A static translator works by accepting Java source code or bytecode and producing platform-dependent native code. This is not much different from a conventional C compiler, but still enjoys Java’s platform independence.

Get your chips — for free! picoJava and Sun’s licensing model

Sun’s picoJava, which directly executes Java bytecodes in hardware, is one of the most exciting recently introduced technologies. Sun has released the design of the picoJava processor free of charge as part of its Community Source License (CSL) program, under which developers need pay a licensing fee only if they ship a product that is developed using the design. This gives hardware designers the ability to quickly develop a processor without restrictive up-front licensing costs. The picoJava design is available for download at the URL listed in Resources. So far more than 200 organizations have accepted the CSL. Indeed, I have downloaded the design, and I am working towards Field Programmable Gate Array (FPGA) implementations.

picoJava features

The picoJava core was developed using Verilog Register Transfer Language (RTL), an industry standard format for digital logic design. Verilog, in many ways, is an object-based language. This is not surprising considering that it is used to model real world logic gates. The entire design runs about 2.4MB of source code.

picoJava is quite sophisticated and is designed for high performance. It supports hardware floating point, a six-stage instruction pipeline, instruction/data caches, and an innovative stack management unit. Moreover, it can compete effectively with traditional processors running JVMs. Preliminary simulation shows that it will meet its design goals and outperform software JVMs for a given clock rate. picoJava might not outperform a high-end Alpha or PowerPC, but in the embedded space, it should do well.

Sun designed picoJava to straddle hardware and software boundaries because there are operations that are too complex to implement fully in hardware. Instance method calls, exception handling, and garbage collection require software support. picoJava provides hardware assistance for each of these operations but eventually generates an interrupt for software handoff. In addition to standard Java bytecodes, picoJava will execute all of the quick bytecodes and extended bytecodes. The extended bytecodes perform operations such as direct memory access, interrupt handling and so on, which are outside the JVM specification.

Garbage collection

Most desktop VMs use traditional mark and sweep collectors, which suspend user threads during a garbage collection phase. This can cause unacceptable delays that result in lost data. As an alternative, advanced concurrent collectors do not suffer such delays and are well-suited to embedded realtime systems. Concurrent collectors operate continuously and suspend user threads for very brief periods, if at all. The result is a robust system with minimal delays. With this in mind, picoJava provides traps for write barriers needed to implement concurrent collectors.

Memory size and class library support

Both the JDK 1.1 and 1.2 class libraries are huge. Total size is many megabytes, which is not much for a modern desktop system, but is unacceptable for many embedded systems where memory may be limited to a megabyte or less. One solution to the size problem is to include only the classes required by a specific system. Indeed, this what Sun has proposed for the Embedded Java specification. Even still, a trivial “Hello World” application, for example, refers to the Object class, which refers to the String class, which in turn refers to the Character class. The Character class alone requires 137 KB, much of which is taken by translation tables needed to support internationalization. A specific VM implementation might choose to support just one character set, but this requires changes to key classes like Character.

Hardware device access

Most devices available to embedded systems — serial ports, parallel ports, and custom interfaces, for example — are memory mapped and require access to specific memory locations in a processor’s address space. However, the Java platform does not define a cross-platform API to access specific memory locations. The obvious solution, and the solution used by JavaSoft in their implementation of Java OS, is to create a class containing native methods that can directly access memory. At first glance, this sounds like a low-performance solution, but it is not necessarily so. In many cases, much of the time-critical code needed for device access involves block data moves. Fast native methods can be designed to move this data more efficiently. This may not be a cross-platform solution, but arguably, device-specific code is rarely cross-platform.

Conclusion

Java can do a lot for embedded systems. Java’s reliability, cross platform support, and extensibility will reduce development costs and increase functionality. It is inevitable that many systems will be implemented in Java, but more infrastructure is needed in terms of Java execution environments. Java may not solve all problems and may not be suitable for all systems, but a large class of systems can benefit from Java’s strengths.

Madhu Siddalingaiah is a consultant and chief technology officer of Science and Engineering Applications Corporation (SEA), a Washington, D.C.-based hardware and software development firm. Madhu has worked with world leaders in diverse fields such as space flight instrumentation, communications receivers, and hardware graphics engines. Madhu has authored two books and speaks regularly at conferences. He also teaches and writes course material for Learning Tree International.