Lynx-EyEDの電音鍵盤 新館

制御とか数学とか駄文とか

LPC800にJVM載せてUARTループバック

Java VMのせてみた

f:id:Lynx-EyED:20130802144748p:plain
とある必要にかられて、Flash ~32kB/RAM ~4kBクラスの小規模コントローラ向けにJVMを実装しました。
Javaを使いたい場合Linuxを搭載した組み込み機器にJavaを載せるのが通常ですがそれが困難な容量の少ないデバイス向けです。

ここではCortex-M0+のLPC812にJVMの移植を試みました。
lynxeyed-atsu/RaVem · GitHub
解釈できるニーモニックは限られてます。
上記のリンクからダウンロードできるのはMDK-ARMプロジェクト一式です。開発環境はMDK-ARMです。32kBまでなら無償のMDK-ARM Liteが使用できます。(要登録)
https://www.keil.com/demo/eval/arm.htm

なお、LPCXpresso LPC812ボード単体だと書き込み、デバッグが出来ません。LPC-Link2をCMSIS-DAPにして使う必要があります。
(参考) LPCマイコン情報:LPC-Link2ノート1-LPC-Link2を各デバッガにしてデバッグする その1 〜Redlink & LPCXPresso, CMSIS-DAPデバッガ & Keil, SEGGER J-Link & Keil〜


Java中間言語生成にはjavac-1.6.0_51を使用しています。(なにか特殊なコードを吐く様にコンパイラを細工したりはしていません)

SDカードなどからバイトコードをブートしても良いですが、始めてやる作業なので不確定要素が多いと困ります。まず力弱くバイトコードを配列にしてソースコードに貼付け、まるごとMDK-ARMでコンパイルます。const char配列にするのでcsv形式で出力ができるバイナリエディタで、コンパイルした*.classファイルをリードすると良いでしょう。

つまり、javacでコンパイル → バイナリをCの配列にする → JVM本体ごとMDK-ARMでコンパイル → LPC812に書き込み
という流れです。

とりあえず、こんなJavaコードを書きました。UARTから1文字受信して、それを表示します。

import java.io.*;
class hoge1{
    public static void main(String[] args){
        int  i;
        System.out.println( "enter any key" );
        while(true){
            try{
                i = System.in.read();
                System.out.print((char)i);
            }
            catch(IOException e){
                System.err.println( "error" );
            }
        }
    }
}

上記をhoge1.javaとして保存。
コンパイルします。

javac hoge1.java

生成されたhoge1.classは下に示すバイトコードが保存されています。

0xCA, 0xFE, 0xBA, 0xBE, 0x00, 0x00, 0x00, 0x32, 0x00, 0x34, 0x0A, 0x00, 0x0C, 
0x00, 0x17, 0x09, 0x00, 0x18, 0x00, 0x19, 0x08, 0x00, 0x1A, 0x0A, 0x00, 0x1B, 0x00, 0x1C, 0x09, 
0x00, 0x18, 0x00, 0x1D, 0x0A, 0x00, 0x1E, 0x00, 0x1F, 0x0A, 0x00, 0x1B, 0x00, 0x20, 0x07, 0x00, 
0x21, 0x09, 0x00, 0x18, 0x00, 0x22, 0x08, 0x00, 0x23, 0x07, 0x00, 0x24, 0x07, 0x00, 0x25, 0x01, 
0x00, 0x06, 0x3C, 0x69, 0x6E, 0x69, 0x74, 0x3E, 0x01, 0x00, 0x03, 0x28, 0x29, 0x56, 0x01, 0x00, 
0x04, 0x43, 0x6F, 0x64, 0x65, 0x01, 0x00, 0x0F, 0x4C, 0x69, 0x6E, 0x65, 0x4E, 0x75, 0x6D, 0x62, 
0x65, 0x72, 0x54, 0x61, 0x62, 0x6C, 0x65, 0x01, 0x00, 0x04, 0x6D, 0x61, 0x69, 0x6E, 0x01, 0x00, 
0x16, 0x28, 0x5B, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x74, 
0x72, 0x69, 0x6E, 0x67, 0x3B, 0x29, 0x56, 0x01, 0x00, 0x0D, 0x53, 0x74, 0x61, 0x63, 0x6B, 0x4D, 
0x61, 0x70, 0x54, 0x61, 0x62, 0x6C, 0x65, 0x07, 0x00, 0x21, 0x01, 0x00, 0x0A, 0x53, 0x6F, 0x75, 
0x72, 0x63, 0x65, 0x46, 0x69, 0x6C, 0x65, 0x01, 0x00, 0x0A, 0x68, 0x6F, 0x67, 0x65, 0x31, 0x2E, 
0x6A, 0x61, 0x76, 0x61, 0x0C, 0x00, 0x0D, 0x00, 0x0E, 0x07, 0x00, 0x26, 0x0C, 0x00, 0x27, 0x00, 
0x28, 0x01, 0x00, 0x0D, 0x65, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x61, 0x6E, 0x79, 0x20, 0x6B, 0x65, 
0x79, 0x07, 0x00, 0x29, 0x0C, 0x00, 0x2A, 0x00, 0x2B, 0x0C, 0x00, 0x2C, 0x00, 0x2D, 0x07, 0x00, 
0x2E, 0x0C, 0x00, 0x2F, 0x00, 0x30, 0x0C, 0x00, 0x31, 0x00, 0x32, 0x01, 0x00, 0x13, 0x6A, 0x61, 
0x76, 0x61, 0x2F, 0x69, 0x6F, 0x2F, 0x49, 0x4F, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6F, 
0x6E, 0x0C, 0x00, 0x33, 0x00, 0x28, 0x01, 0x00, 0x05, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x01, 0x00, 
0x05, 0x68, 0x6F, 0x67, 0x65, 0x31, 0x01, 0x00, 0x10, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 
0x6E, 0x67, 0x2F, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x01, 0x00, 0x10, 0x6A, 0x61, 0x76, 0x61, 
0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x01, 0x00, 0x03, 0x6F, 
0x75, 0x74, 0x01, 0x00, 0x15, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x69, 0x6F, 0x2F, 0x50, 0x72, 
0x69, 0x6E, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x3B, 0x01, 0x00, 0x13, 0x6A, 0x61, 0x76, 
0x61, 0x2F, 0x69, 0x6F, 0x2F, 0x50, 0x72, 0x69, 0x6E, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6D, 
0x01, 0x00, 0x07, 0x70, 0x72, 0x69, 0x6E, 0x74, 0x6C, 0x6E, 0x01, 0x00, 0x15, 0x28, 0x4C, 0x6A, 
0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x3B, 
0x29, 0x56, 0x01, 0x00, 0x02, 0x69, 0x6E, 0x01, 0x00, 0x15, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 
0x69, 0x6F, 0x2F, 0x49, 0x6E, 0x70, 0x75, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x3B, 0x01, 
0x00, 0x13, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x69, 0x6F, 0x2F, 0x49, 0x6E, 0x70, 0x75, 0x74, 0x53, 
0x74, 0x72, 0x65, 0x61, 0x6D, 0x01, 0x00, 0x04, 0x72, 0x65, 0x61, 0x64, 0x01, 0x00, 0x03, 0x28, 
0x29, 0x49, 0x01, 0x00, 0x05, 0x70, 0x72, 0x69, 0x6E, 0x74, 0x01, 0x00, 0x04, 0x28, 0x43, 0x29, 
0x56, 0x01, 0x00, 0x03, 0x65, 0x72, 0x72, 0x00, 0x20, 0x00, 0x0B, 0x00, 0x0C, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x00, 
0x1D, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x2A, 0xB7, 0x00, 0x01, 0xB1, 0x00, 0x00, 
0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x09, 
0x00, 0x11, 0x00, 0x12, 0x00, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x02, 0x00, 0x03, 
0x00, 0x00, 0x00, 0x26, 0xB2, 0x00, 0x02, 0x12, 0x03, 0xB6, 0x00, 0x04, 0xB2, 0x00, 0x05, 0xB6, 
0x00, 0x06, 0x3C, 0xB2, 0x00, 0x02, 0x1B, 0x92, 0xB6, 0x00, 0x07, 0xA7, 0xFF, 0xF1, 0x4D, 0xB2, 
0x00, 0x09, 0x12, 0x0A, 0xB6, 0x00, 0x04, 0xA7, 0xFF, 0xE5, 0x00, 0x01, 0x00, 0x08, 0x00, 0x17, 
0x00, 0x1A, 0x00, 0x08, 0x00, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x07, 0x00, 0x00, 
0x00, 0x06, 0x00, 0x08, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x0A, 0x00, 0x17, 0x00, 0x0E, 0x00, 0x1A, 
0x00, 0x0C, 0x00, 0x1B, 0x00, 0x0D, 0x00, 0x23, 0x00, 0x0E, 0x00, 0x13, 0x00, 0x00, 0x00, 0x07, 
0x00, 0x02, 0x08, 0x51, 0x07, 0x00, 0x14, 0x00, 0x01, 0x00, 0x15, 0x00, 0x00, 0x00, 0x02, 0x00, 
0x16, 0x00, 0x00, 0x00

上記バイトコードを、MDK-ARMプロジェクトの中のravem.cにvm_array[]という配列名で記述し、コンパイル。
USB-UARTとLPC812を接続します。

PIO0_4 -> UART0_TXD
PIO0_0 -> UART0_RXD

無事動作すれば、下の様になります。
f:id:Lynx-EyED:20130802152912p:plain


開発風景(笑
f:id:Lynx-EyED:20130802154506j:plain

おまけ:マルチスレッドっぽい何かを実装してみた

ノンプリエンプティブ型でマルチスレッドを実装してみました。
このご時世にノンプリエンプティブとか何考えてるんですかwと言われかねませんが、、そもそも、すでにCortex-M0+にとってJVMが高負荷であり、メモリが限られていて、かつ10も20もスレッドを持つ意義が感じられない・持つことができない*1デバイスに複雑なTCBを持つプリエンプティブ型を採用するのは利点がないように思えたからです。このデバイスでマルチスレッドを持つとしてもメソッドの負荷によりますが2ないし3個が現実的でしょう。それならばノンプリエンプティブ型のほうが、トータルで見た開発時間が少なくてすみます。
Javacで以下のコードをコンパイルします

class hoge1 extends Thread{
    
        public void run() {
            while(true) {
                System.out.print("thread Number=");
                System.out.println(getName());
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                }
            }
        }
    
    
        public static void main(String[] args){
            System.out.println("Thread test");
            
            hoge1 thread1 = new hoge1();
            hoge1 thread2 = new hoge1();
            
            thread1.start();
            thread2.start();
        }
    
}

ここで、

System.out.println("thread Number=" + getName());

と記述した方が処理が高速になるよね?と考えた方、正解です。
すみません。StringBuilderを実装していないので、Stringの連結ができないんですよ。(ぉぃ
そのうちしますよそのうち
バイトコードは以下のようになります。

0xCA, 0xFE, 0xBA, 0xBE, 0x00, 0x00, 0x00, 0x32, 0x00, 0x35, 0x0A, 0x00, 0x0F, 
	0x00, 0x1B, 0x09, 0x00, 0x1C, 0x00, 0x1D, 0x08, 0x00, 0x1E, 0x0A, 0x00, 0x1F, 0x00, 0x20, 0x0A, 
	0x00, 0x0B, 0x00, 0x21, 0x0A, 0x00, 0x1F, 0x00, 0x22, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x03, 0xE8, 0x0A, 0x00, 0x0B, 0x00, 0x23, 0x07, 0x00, 0x24, 0x07, 0x00, 0x25, 0x0A, 0x00, 0x0B, 
	0x00, 0x1B, 0x0A, 0x00, 0x0B, 0x00, 0x26, 0x08, 0x00, 0x27, 0x07, 0x00, 0x28, 0x01, 0x00, 0x06, 
	0x3C, 0x69, 0x6E, 0x69, 0x74, 0x3E, 0x01, 0x00, 0x03, 0x28, 0x29, 0x56, 0x01, 0x00, 0x04, 0x43, 
	0x6F, 0x64, 0x65, 0x01, 0x00, 0x0F, 0x4C, 0x69, 0x6E, 0x65, 0x4E, 0x75, 0x6D, 0x62, 0x65, 0x72, 
	0x54, 0x61, 0x62, 0x6C, 0x65, 0x01, 0x00, 0x03, 0x72, 0x75, 0x6E, 0x01, 0x00, 0x0D, 0x53, 0x74, 
	0x61, 0x63, 0x6B, 0x4D, 0x61, 0x70, 0x54, 0x61, 0x62, 0x6C, 0x65, 0x07, 0x00, 0x24, 0x01, 0x00, 
	0x04, 0x6D, 0x61, 0x69, 0x6E, 0x01, 0x00, 0x16, 0x28, 0x5B, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 
	0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x3B, 0x29, 0x56, 0x01, 0x00, 
	0x0A, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6C, 0x65, 0x01, 0x00, 0x0A, 0x68, 0x6F, 
	0x67, 0x65, 0x31, 0x2E, 0x6A, 0x61, 0x76, 0x61, 0x0C, 0x00, 0x10, 0x00, 0x11, 0x07, 0x00, 0x29, 
	0x0C, 0x00, 0x2A, 0x00, 0x2B, 0x01, 0x00, 0x0E, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x4E, 
	0x75, 0x6D, 0x62, 0x65, 0x72, 0x3D, 0x07, 0x00, 0x2C, 0x0C, 0x00, 0x2D, 0x00, 0x2E, 0x0C, 0x00, 
	0x2F, 0x00, 0x30, 0x0C, 0x00, 0x31, 0x00, 0x2E, 0x0C, 0x00, 0x32, 0x00, 0x33, 0x01, 0x00, 0x1E, 
	0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x72, 
	0x75, 0x70, 0x74, 0x65, 0x64, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x01, 0x00, 
	0x05, 0x68, 0x6F, 0x67, 0x65, 0x31, 0x0C, 0x00, 0x34, 0x00, 0x11, 0x01, 0x00, 0x0B, 0x54, 0x68, 
	0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x73, 0x74, 0x01, 0x00, 0x10, 0x6A, 0x61, 0x76, 0x61, 
	0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x01, 0x00, 0x10, 0x6A, 
	0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x01, 
	0x00, 0x03, 0x6F, 0x75, 0x74, 0x01, 0x00, 0x15, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x69, 0x6F, 
	0x2F, 0x50, 0x72, 0x69, 0x6E, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x3B, 0x01, 0x00, 0x13, 
	0x6A, 0x61, 0x76, 0x61, 0x2F, 0x69, 0x6F, 0x2F, 0x50, 0x72, 0x69, 0x6E, 0x74, 0x53, 0x74, 0x72, 
	0x65, 0x61, 0x6D, 0x01, 0x00, 0x05, 0x70, 0x72, 0x69, 0x6E, 0x74, 0x01, 0x00, 0x15, 0x28, 0x4C, 
	0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 
	0x3B, 0x29, 0x56, 0x01, 0x00, 0x07, 0x67, 0x65, 0x74, 0x4E, 0x61, 0x6D, 0x65, 0x01, 0x00, 0x14, 
	0x28, 0x29, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x74, 0x72, 
	0x69, 0x6E, 0x67, 0x3B, 0x01, 0x00, 0x07, 0x70, 0x72, 0x69, 0x6E, 0x74, 0x6C, 0x6E, 0x01, 0x00, 
	0x05, 0x73, 0x6C, 0x65, 0x65, 0x70, 0x01, 0x00, 0x04, 0x28, 0x4A, 0x29, 0x56, 0x01, 0x00, 0x05, 
	0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x20, 0x00, 0x0B, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x11, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1D, 0x00, 
	0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x2A, 0xB7, 0x00, 0x01, 0xB1, 0x00, 0x00, 0x00, 0x01, 
	0x00, 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x14, 
	0x00, 0x11, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x60, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 
	0x00, 0x1F, 0xB2, 0x00, 0x02, 0x12, 0x03, 0xB6, 0x00, 0x04, 0xB2, 0x00, 0x02, 0x2A, 0xB6, 0x00, 
	0x05, 0xB6, 0x00, 0x06, 0x14, 0x00, 0x07, 0xB8, 0x00, 0x09, 0xA7, 0xFF, 0xE8, 0x4C, 0xA7, 0xFF, 
	0xE4, 0x00, 0x01, 0x00, 0x12, 0x00, 0x18, 0x00, 0x1B, 0x00, 0x0A, 0x00, 0x02, 0x00, 0x13, 0x00, 
	0x00, 0x00, 0x1A, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x06, 0x00, 0x12, 0x00, 
	0x08, 0x00, 0x18, 0x00, 0x0A, 0x00, 0x1B, 0x00, 0x09, 0x00, 0x1C, 0x00, 0x0A, 0x00, 0x15, 0x00, 
	0x00, 0x00, 0x07, 0x00, 0x02, 0x00, 0x5A, 0x07, 0x00, 0x16, 0x00, 0x09, 0x00, 0x17, 0x00, 0x18, 
	0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x21, 
	0xBB, 0x00, 0x0B, 0x59, 0xB7, 0x00, 0x0C, 0x4C, 0xBB, 0x00, 0x0B, 0x59, 0xB7, 0x00, 0x0C, 0x4D, 
	0x2B, 0xB6, 0x00, 0x0D, 0x2C, 0xB6, 0x00, 0x0D, 0xB2, 0x00, 0x02, 0x12, 0x0E, 0xB6, 0x00, 0x06, 
	0xB1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x13, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x06, 0x00, 0x00, 0x00, 
	0x11, 0x00, 0x08, 0x00, 0x12, 0x00, 0x10, 0x00, 0x14, 0x00, 0x14, 0x00, 0x15, 0x00, 0x18, 0x00, 
	0x17, 0x00, 0x20, 0x00, 0x18, 0x00, 0x01, 0x00, 0x19, 0x00, 0x00, 0x00, 0x02, 0x00, 0x1A, 0x00, 
	0x00, 0x00

コンパイルして実行すると、UARTでマルチスレッドっぽくなっているところが確認できると思います。
f:id:Lynx-EyED:20130809213540p:plain


◆参考
Chapter 4. The class File Format
Javaのhello worldの読み方 | mwSoft




*1:LPC81xは省メモリで安価なデバイスですので機能ごとに複数のデバイスに分けて平行動作させたほうがデバッグ、機能安全の観点からもメリットがありますし、省電力デバイスなのでパワーマネージメントの設計に注力した方が良いと思われます