Cmod A7ボード(FPGA)を使って開発しているオリジナル16ビットCPUコアMoscoviumの開発環境として、Blocklyを使える用に整備しました(以下記事を参照)。
「#31-2 Moscoviumの開発環境(ワクワク)」
「#31-3 Moscoviumの開発環境(アゲアゲ)」
「#31-6 Moscoviumの開発環境(カスカス)」
今回取り組むのは、文字列操作機能(ブロック)の拡張です。
本HPのサンプルプログラムは、シリアルモニタ(UART)からコマンドを受け付けて、マイコンの操作や機能の設定を実施する物が多いです。例えばこちらをご確認ください(ほんの一例)。
シリアルモニタから受信する情報は文字列データなので、受信した文字列データを解釈してコマンドを判別したり、その引数を抜き出す作業が必要となります。
例えば以下の様なコマンドを解釈する事になります。
コマンド例)wr1,<adr_hex4>,<dat_dex2>
この様な機能を実現するためには、どのようなブロックが必要でしょうか?順番に検討して行きましょう。
前節で示した例の様に、UARTから受信した文字列データの先頭がコマンドを示す文字列だとする場合、受信文字列<vstr>とコマンド文字列<cmnd>とを比較して、どんなコマンドなのか?を判定したいところです。
しかし、C言語標準関数のstrcmp()関数等、通常の文字列比較関数では、うまくありません。
例)通常の文字列比較
この様な場合、前方一致比較が使えると、文字列先頭部分との比較ができます。
前方一致比較:
この機能があれば、受信したコマンドラインが「wr1」コマンドであるか否かを判別する事ができます。
Moscovium用のカスタムブロックとしてはcompare leftブロックを用意しました。詳細についてはリンク先のマニュアルでご確認ください。
※そういう意味では、C言語標準関数のstrncmp()関数やstrlen()関数も抽象度が低い部類なので、カスタムブロックとしては前方一致比較(compare left)と言う抽象度が高めなブロックとして用意しました。
コマンドが判別出来たら次は、コマンドと、その引数を分離するため、カンマ(,)などの区切り文字を検索する必要が生じます。C言語標準関数では、strchr()関数に相当します。
例)1文字検索
strchr()関数で機能としては充足するのですが、引数が複数個ある場合、区切り文字も複数個存在するため、もう一工夫必要です。1回見つけた区切り文字はスキップして、その次(2個目)の区切り文字を探したいのです。
この課題は、文字列<vstr>にオフセットを指定することができれば解決できます。オフセット値<ofst>=0〜strlen (<vstr>)として、以下の通り、<vstr>+<ofst>を定義します。
例)オフセット付き文字列参照:<vstr>+<ofst>
<ofst> | <vstr>="wr1,ffa4,9e"の場合 |
−3 | <vstr>+<ofst>="wr1,ffa4,9e" |
0 | <vstr>+<ofst>="wr1,ffa4,9e" |
1 | <vstr>+<ofst>="r1,ffa4,9e" |
4 | <vstr>+<ofst>="ffa4,9e" |
9 | <vstr>+<ofst>="9e" |
20 | <vstr>+<ofst>="" |
オフセット付き文字列参照を使うと、区切り文字などの検索は以下の様に実現できます。
例)オフセット付き1文字検索
Moscovium用のカスタムブロックとしてはfindブロックを用意しました。詳細についてはリンク先のマニュアルでご確認ください。
機能自体は、既に追加してあったカスタムブロックである、atoi/xtoiブロックで実現可能です。
これらのブロックは、文字列の先頭から10進数文字列(atoi)または16進数文字列(xtoi)を解釈して、それぞれの数値として認識できない文字が見つかった時点で解釈を終了します。
<vstr>="wr1,ffa4,9e"等の例では、変換したい16進数文字列(例えばffa4)が、文字列<vstr>の先頭に存在しないので、xtoiブロックを使用することができません。
前節の様に、オフセット付き文字列参照ができれば、解決することができます。
例)オフセット付き文字列参照と、16進数文字列→数値変換(xtoi)
Moscovium用のカスタムブロックとしてはpointerブロックを用意しました。詳細についてはリンク先のマニュアルでご確認ください。
※atoiブロックとxtoiブロックを、各々オフセット付き文字列型に改造するより、pointerブロックを用意しておいた方が汎用的に応用が利くでしょう。
以上の節による検討にて、シリアルモニタ(UART)から受信した文字列を解釈して、コマンドと引数を取り出す文字列操作を拡張することができました。
その他、オフセット付き文字列操作で便利そうなブロックを追加しておきました(下図)。
詳しくは、こちらのマニュアルでご確認ください。
追加した文字列操作ブロックを使う場合、Blockly.XML→Moscovium.ASM変換スクリプトも、対応した最新バージョンを使う必要があります。こちらのリンクから最新バージョンをダウンロードしてお使いください。
Moscovium用にカスタマイズしたBlocklyデモ環境にも、修正が必要です。こちらの手順6)を最新ファイルに更新してください。
今回追加したオフセット付き文字列操作ブロックを使って、Blocklyでプログラムを組み上げてみました。動作としては、こちら(「#27-4 移植後の検証」)の記事で作成したmcoc_blink.asm相当のプログラムとしました。以下にリンクしておきます。
完成プログラム:blky_mcoc_blink.xml (PNG)
元のアセンブラプログラム(参考):mcoc_blink.asm
Blocklyで最初に作ったLチカプログラムは、本当に単純にLEDをチカチカさせるだけ、でした。今回のプログラムはLEDをチカチカさせるだけに留まらず、シリアルモニタ経由でコマンドを受け付け、Moscoviumマイコン内の任意のアドレスへワードアクセスすることができ、簡易的なモニタプログラムとしても機能します。
シリアルモニタで受け付けるコマンドの仕様は、元のプログラム(mcoc_blink.asm)と互換にしてありますので、こちらの説明をご確認ください。
ほぼ同じ動作となる、アセンブラ版とBlockly版のプログラムが完成しましたので、各々アセンブルして比較してみました。以下表をご覧ください。
mcoc_blink(_blky) |
XML→ASM 変換時間 |
アセンブル 時間 |
コードサイズ |
アセンブラ版(.asm) | − | 65s | 1,558バイト |
Blockly版(.xml) | 30s | 140s | 4,160バイト |
Blockly版はアセンブル時間に加えて、Blockly.XML→Moscovium.ASM変換スクリプトの変換時間も、かかってきます。その上、Blockly版はコードサイズも大きくなっているため、アセンブル時間も長くなっています。
変換もアセンブラも、Olive+によるスクリプト処理のため、実行速度が遅いのは、止む無しです。
コードサイズ的にも大きな差が付きました。
そりゃ、そうです。アセンブラ版は、「抽象度の低いアセンブラで、なるべく効率が上がる様に」人間がプログラミングしたコードです。一方のBlockly版は、抽象度が高い代わりに、コード効率はアセンブラに比べて低くなるのは当然のことです。
CPUと言うのは、プログラム(命令)のフェッチにボトルネックがあります。命令を取り出し(フェッチ)て来ない限り、それがどんな命令で、どういう動作をすれば良いのか、知ることができません。CPU内部の動作を、どんなに高速化しても、命令フェッチ速度で頭を押さえられてしまうのです。
上表の結果に戻るとBlockly版は、同じ動作をするアセンブラ版に比べて約2.7倍(≒4,160÷1,558[バイト])のコードサイズ比があるため、プログラムの実行速度も相応に遅くなっていると考えられます。
後は、何に重きを置くか?と言う事になります。以下表に、アセンブラ版とBlockly版の特徴をまとめてみました。
特徴 | 長所 | 短所 |
アセンブラ |
高速 コード効率が高い |
プログラムを組み難い 可読性が低い |
Blockly |
プログラムを組み易い 可読性が高い |
低速 コード効率が低い |
各々、長所の裏返しが短所となって交差していることが解ります。一意に「どちらが優れている」と決められるものではない、と言う事です。
ただ、Blockly導入に際して議論した「抽象度を上げる」事に関しては、正に上表Blocklyの長所に繋がっています。
もっと大規模だったり、複雑な動作を要求されるプログラムを作る、となった時、「これ以上はアセンブラでは無理!勘弁してぇ〜!」ってなる時が必ずあるはずです。そんな時、少なくともアセンブラよりはBlocklyの方が、より大規模/複雑なプログラムを容易に組み上げることができるはずです。
そうして組み上げたプログラムは、可読性も高いため、開発終了後、しばらく経った後に見ても、比較的容易に全体の流れを読み解くことができますし、何より、作者以外の人間が見てもプログラムの動作を理解し易いです。
多少コード効率が低かろうが、実行速度が遅かろうが、抽象度の高い開発環境を使用する場合の大きな長所です。
こちらのプログラムをBlockly.XML→Moscovium.ASM変換後、アセンブルして、適切なMoscoviumコアを搭載したMCUと共にCmod A7ボード(FPGA)に書き込んでください。コードサイズが4Kバイトを超えているので、命令ROMを8Kバイト以上搭載しているデザインを使用してください。例えば、mcoc115sv0832.vh等が該当します(型名一覧表はこちら)。
あるいは、〜.vhファイル内の以下設定を有効にしてお使いください。
`define MCOC_ROM_8K
または、
`define MCOC_ROM_16K
Cmod A7ボード用Moscoviumマイコンの設計データ(Vivadoプロジェクト)は、GitHubにて配布しております。以下バナーよりアクセスしてください。
以下リンクも、ご参照ください。
PCでTera Term等の通信ソフトウェアを実行し、Cmod A7ボードの通信ポートを開いて接続してください。以下、実験中の様子です(写真は使い回し)。
デザインとプログラムをCmod A7ボードへ書き込んだ時点で、RGB LEDが色を変化させながら、チカチカしているはずです。光り方については、こちらの解説または動画を参照してください。
実験を進めましょう。
実験サンプル:【 】内はシリアルモニタからの送信データ
【who】
OK+ id code: 1150
OK+ version: 0222
OK+ fcpu kHz:24000
OK+ edition: 5356 SV
OK+ rom top: 0000
OK+ rom size:2000
OK+ ram top: 5000
OK+ ram size:8000
OK: blky_mcoc_blink ver.1.00
プログラムの名前とバージョンに加えて、IDREGユニットのI/Oレジスタを読み出して表示する【who】コマンドを実行しました。上の表示サンプルは、mcoc115sv0832.vhを使った場合の例です。他のデザインを使った場合は、異なる結果が表示されます。
【rd,f018】
OK: read[0xf018] = 0x0d9c
【rd,f01a】
OK: read[0xf01a] = 0x01c7
Moscoviumマイコンのアドレス0xf010〜0xf01f範囲は、システムタイマSYSTIMユニットのI/Oレジスタ領域です。
上の実験サンプルでは、マイクロ秒カウンタsytmmcrレジスタ(アドレス0xf018、32ビット)を読み出しました。上位16ビットがアドレス0xf018、下位16ビットがアドレス0xf01aから読み出せるので、上の実行結果だと、0x0d9c_01c7μs=228,327,879μs≒3.8分という値が読み出せた事になります。
なお、sytmmcrレジスタはリセット解除からの経過時間[μs]が読み出せるので、実行するタイミング次第で異なる値が返ります。
【wr,f010,01】
OK: write[0xf010] = 0x0001
sytmctlレジスタのRSTビット=1を書き込んで、SYSTIMユニットのカウンタをリセットしました。
【rd,f018】
OK: read[0xf018] = 0x003f
【rd,f01a】
OK: read[0xf01a] = 0xec7c
【wr】コマンドによってsytmmcrレジスタもリセットされたので、【wr】コマンドから【rd】コマンドまでの経過時間[μs]が返って来たはずです。
この程度の動作が確認できたら、追加したオフセット付き文字列操作ブロックの動作確認としては十分でしょう。
Lチカの様子は、以下動画でご確認いただけます。
BlocklyでMoscoviumマイコンのプログラミングを実施するデモ動画は、以下をご覧ください。
いよいよ、Blocklyで実用的なMoscoviumアプリケーションを作成できる環境が整いました。Blocklyは、「Moscoviumで遊ぶためのおもちゃ」ではなく、実用的な開発ツールになり得るという事です。素晴らしい🎊\(1^−^)/🎉。