MiMicのネイティブ関数コールインタフェイス

MiMicRemoteMCUのファームフェア自作の関数を追加して、それをJavascriptAPIなどの高レベルAPIで呼び出すことが出来るようになりました。この機能は、MiMic/1.1.0(MiMicRemoteMCU/1.1)から使用できます。

 

何が出来るのか

例えば、以下の様なC言語で実装した関数を、Javascriptから呼び出すことが出来ます。この例では、ストリームから受信した32bit Int値を、そのままエコーバックします。

C言語のユーザ関数(Native function)

NyLPC_TBool jp_nyatla_native_sample(NyLPC_TcMiMicVM_t* i_vm)
{
  NyLPC_TUInt32 v;
  if(!NyLPC_cMiMicVM_sget(i_vm,&v)){
    return NyLPC_TBool_FALSE;
  }
  //write uint32 value from stream.
  if(!NyLPC_cMiMicVM_sput(i_vm,v)){
    return NyLPC_TBool_FALSE;
  }
  return NyLPC_TBool_TRUE;//OK
}

呼び出す為のJavascript(Caller)

function init(){
  var mcu=new LPCXpresso1769.Mcu("192.168.128.39");
  var ni=new LPCXpresso1769.Ni(mcu);
  var ret={stream:null};
  ni.call(0x39000000,{stream:[31319]},ret);
  alert(ret.stream[0]);
}

ネイティブ関数の仕様

ネイティブ関数は、入力ストリーム、又はVMのワークレジスタから引数を受け取ります。また、出力ストリーム、又はVMのワークレジスタに戻り値を返します。値の型は、32bit int型のみが扱えます。

どのネイティブ関数を呼び出すかは、ファンクションIDによって決定します。ファンクションIDは、32bitのint値です。ファンクションIDと関数のマッピングは、ファームウェアの中に定義します。

ネイティブ関数のスタックサイズは、約256バイトです。

実装方法

ネイティブ関数は、次のステップで実装します。

  1. ファームフェアに関数を実装する
  2. 関数テーブルに関数を登録する
  3. ファームウェアの更新
  4. JavascriptAPIのLPCXpresso1769.Niクラスからの実行テスト

 

1.ファームウェアに関数を定義する

ネイティブ関数のプロトタイプは、次の通りです。

typedef NyLPC_TBool (*NyLPC_TNativeFunction)(NyLPC_TcMiMicVM_t* i_vm);

戻り値は、関数の正常終了/異常終了のフラグです。FALSEを返すと、呼出元のMiMicVMは後続のバイトコードの実行を放棄し、エラーコードMVM_RUNTIME_NG_CALLを返却して終了します。

i_vmは、この関数を呼び出したMiMicVMのインスタンスです。関数は、i_vm->wm[]の値を操作することが出来ます。

次のコードは、wm0に1を加算して返す関数を定義します。

NyLPC_TBool sample(NyLPC_TcMiMicVM_t* i_vm)
{
  i_vm->wm[0]+=1;
  return NyLPC_TBool_TRUE;//OK
}

関数は、native_function.cに定義してください。自分で関数を追加する場合は、native_function.hをインクルードするようにしてください。

 

2.関数テーブルに関数を登録する

関数テーブルは、native_function.cのnaitive_function_tableで定義します。次の様に登録します。

const struct TNativeFunctionItem naitive_function_table[]=
{
  {0x39000000,"jp.nyatla.native_sample",jp_nyatla_native_sample},
  {0x00000001,"your sample function",sample},
  //
  // add your native function here!
  //
  {0x39FFFFFF,NULL,NULL} //This is terminator.
};

1番目のフィールドはファンクションIDです。関数を識別するID値になります。2番目のフィールドは関数の文字列名です。現在は、この値に意味はありません。3番目のフィールドが、関数ポインタになります。

ファンクションIDにはビットフィールドがあります。基本的にLocalIDを使用してください。他の予約値は使用しないでください。ビットフィールドの説明を以下に示します。

bit field name value
31-24 Address type
  • 0×00:LocalID ローカルID
  • 0×01:MAC-OUI グローバルID
  • 0×02:MiMic Domain グローバルID
  • 0×39:MiMic Reserved グローバルID
  • other – reserved
23-0 Number Address Typeの値で意味が異なる。

  • LocalID – 23-0に、ユーザ定義IDが含まれていることを示す。他のシステムと重複の可能性があるので注意すること。
  • MAC-OUI – 23-0に、MACAddrのOUI値が含まれていることを示す。OUI所有事業者が、ベンダ固有の関数を定義する場合に使用する。
  • MiMic Domain – MiMicDomainモード。23-0に、MiMic projectがユーザに割り当てたIDが格納されていることを示す。
  • MiMic Reserved – MiMic予約値。

グローバルIDのために、MiMicDoamainのIDが必要な方はお知らせください。発行します。

 

3.ファームウェアの更新

実装が完了したら、ファームウェアをLPCXpresso1769へ書き込んで更新してください。ファームウェアを更新すると、設定値がリセットされるので注意して下さい。

 

4.JavascriptAPIからの実行テスト

新しいファームウェアのMiMicに接続できることを確認したら、次のHtmlを作成して、実行します。IPアドレスの部分は、適切に変更してください。

LPCXpresso1769.Niクラスについては、こちらのドキュメントを参照してください。

<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="../MiMicCore.js"></script>
<script src="../LPCXPresso1769.All.js"></script>
<script type="text/javascript">

function init(){
  var mcu=new LPCXpresso1769.Mcu("192.168.0.39");
  var ni=new LPCXpresso1769.Ni(mcu);
  var ret={wm0:null};//create a result holder.
  ni.call(0x00000001,{wm0:38},ret);
  alert(ret.wm0);//show result
}
</script>
</head>
<body onload="init();"><h1>native call test</h1></body>

次のようにAlertが表示されれば、成功です。

応用編

MiMicのネイティブコール関数は、WMレジスタ(0-7)のほかに、HTTPストリームから値を得ることも出来ます。HTTPストリーム関数とのデータの交換には、NyLPC_cMiMicVM_sget関数と、NyLPC_cMiMicVM_sput関数を使います。

  NyLPC_TUInt32 v;
  //read uint32 value from stream.
  if(!NyLPC_cMiMicVM_sget(i_vm,&v)){
    return NyLPC_TBool_FALSE;
  }
  //write uint32 value from stream.
  if(!NyLPC_cMiMicVM_sput(i_vm,v)){
    return NyLPC_TBool_FALSE;
  }

NyLPC_cMiMicVM_sget関数は、httpストリームから32bit int値を1個読み出します。この値は、call関数の第2引数の入力パラメタのstreamに、配列で与えます。1回目のコールで1番目、2回目のコールで2個目のパラメータが得られます。

LPCXpresso1769.Ni.call(FUNCTION_ID,{stream:[1,2,3...]},OUTPUT)

NyLPC_cMiMicVM_sput関数は、httpストリームへ32bit int値を1個書き出します。この値は、call関数の第3引数の出力パラメタにstreamが有る場合に、配列で返されます。1回目のコールで1番目、2回目のコールで2個目の要素の値を設定します。

var r={stream:null};
LPCXpresso1769.Ni.call(FUNCTION_ID,INPUT,r);

現在は32bit int以外の値を関数に引き渡すことはできません。文字列などは、32bit値に変換して渡す必要があります。

Inside MiMic Native Function Interface

Mimicのネイティブ関数コールは、MiMicRemoteMCU/1.1 から追加された、MiMicVMのCALL命令を使用して実装されています。

CALL命令は、IDに一致する組み込み関数を、VMのインスタンスを引数にして呼び出します。

ここで紹介したLPCXpresso1769.Niクラスは、CALL命令の前後にVMの状態設定コードで挟んで実装したものです。実行されているMiMicILは、以下の構造をしています。

;WMレジスタの初期化
LD #0 (WM0の値)
LD #1 (WM1の値)
; : WMの初期化を必要な数だけ実行
CALL #0
;WM値の出力
SPUT #0
SPUT #1
; : WMの出力を必要な数だけ実行
EXIT
.END
.D32 (streamの値0)
.D32 (streamの値1)
; :

ネイティブ関数コール自体はMiMicILレベルで行われるので、必要なら、複数のネイティブ関数コールを組み合わせて実行することも出来ます。

Comments are closed, but trackbacks and pingbacks are open.