2015年12月12日土曜日

「製品開発におけるソフトウェアの品質保証」と題してプレゼンテーションを実施させて頂きました

CQ出版社Interface 2015年11月号「レベルアップ!フリーのCプログラミング道具箱」の第2章「シンプル・便利・基本的!フリー・ソフトで高品質プログラミング」にも関連する内容で、「製品開発におけるソフトウェアの品質保証」と題して、酔漢さんの主催するワークショップでプレゼンテーションを実施させて頂きました。


近年、幾つかのプロジェクトを見ていて感じている事ですが、製品品質を確保する上で本質的に欠かせない内容に触れる事なく、何か別の(形式上の)作業に振り回されているうちに、肝心の品質保証が不十分になってしまうケースがあるようです。今回のプレゼンテーションでは、現代的な製品に欠かす事のできないソフトウェアの品質保証について、実際に現場で起きた事をざっくりデフォルメして説明させて頂きました。


先日のInterfaceの記事でも少し触れましたが、設計がどうして必要なのか、実装は何に対するものなのか、実装の正しさは何によって証明すべきものなのか、など、これらの関係について明確な思想を持っておく事が非常に重要です。


しかし、実際に現場で色々な作業成果物を見てみると、何とも残念な結果になっている事が少なくありません。それらがどのような背景で起きるのかについて触れるとともに、どのようなアプローチが実際に現場で有効であるのかについて、ヒントを示すような内容としました。

例えば、製品の見た目や大きさで設計対象物の大きさを判断しない事、など、少し考えてみれば当たり前の事ですが、混乱が繰り返し起こるような現場では有用な示唆になると思います。この「物事は見た目ほど単純ではない」というのは、色々な場面で使えます。


資料は、後日酔漢さんのウェブページで公開予定ですのでお楽しみに。
https://signalbottom.wordpress.com/

2015年12月16日追記:(リンク)
https://signalbottom.wordpress.com/2015/12/13/2015%E5%B9%B411%E6%9C%88%E3%83%AF%E3%83%BC%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%83%E3%83%97%E3%81%BE%E3%81%A8%E3%82%81/ に公開頂きました。

2015年12月29日追記:(要約)
製品の品質を担保するための活動は、その製品の企画構想、仕様、設計、実装、内部検証、外部検証、文章化、クロージングなど、製品開発の全てのフェーズに渡るのが通例である。しかしながら、実際の開発現場では、各フェーズの関連性を見い出せず、結果的に品質保証の視点が完成したシステムに対する外から見た挙動を評価する事に終始するようなケースもみられる。このような背景を踏まえた上で、本発表ではシステムの内部品質にフォーカスし、特定の指標を用いた設計や実装の品質保証について概略を述べる。

2015年11月29日日曜日

「ちょっとすごいロガー(NinjaScan-Light) スイッチサイエンス版」を「ちょっとへぼいケース」に入れる話

スイッチサイエンスさんで人気沸騰中のちょっとすごいロガー(NinjaScan-Light) スイッチサイエンス版を購入しました。基板好きにはこの凝縮感がたまりません。

早速ですが、気軽に外に持ち出せるようにと何らかのケースに入れる事にしました。部屋の中を見回して色々な小物ケースを探したのですが、しっくりはまったのはフリスクのケース。


ただ、ケースの中で基板がガタガタ動いてしまってはセンサの値に影響を与えます。かといって、両面テープでがっちり固定する気にもなれなかったので、今回はスコッチが出しているジェル状の接着物を使用する事にしました。

残念ながら耐熱温度仕様などは書かれていません。もしかしたら後でまずいことになるかもしれませんので、同じようにやってみようという方は自己責任でお願いします。


当初はu-bloxのモジュールが上を向く形で実装するつもりでしたが、この面を接着面として使った方が具合が良いと判断。u-bloxのモジュールに接着物を貼り付けてフリスクケースに固定します。


これでばっちりケースに入りました。CON1がケースの蓋に当たるので、蓋の肉厚を調整して対処します。


USBのコネクタを外に出すために穴もあけておいて下さい。
私の場合・・・もっと綺麗にやり直したい。バリとか取っておかないと後で色々困ります・・・俺。

2015年10月31日土曜日

システムにおけるレイテンシーとスループットの話題

レイテンシーとは、あるシステムに何らかの入力を与え、それに対応する何らかの出力が出てくるまでの時間(遅延量)を指します。例えば、ある処理の実行を指令してから実際の結果が得られるまでに250ミリ秒かかったとすると、それは250ミリ秒のレイテンシーを持つシステムという事になります。それではここで、この時のスループットについて考えてみましょう。

以下の図は、横軸が時間で、250ミリ秒かけてある処理を4回実行した時の様子を示したものです。


上記の図に示した「ある処理」で取り扱われるデータが32MBだったと仮定します。このシステムの場合、「ある処理」を直列に実行しており、スループット[MB/s]は(32[MB] x 4) / 1[s] = 128[MB/s]となります。

さて、ハードウェアであってもソフトウェアであっても、最初に設計実装した処理のスループットが想定を下回り、結果的に何らかの対策が必要になる場合があります。必要なスループットに達していない場合、所望の処理を並列に実行する事でスループットを改善しますが、これは一体どのようなメカニズムによるものなのでしょうか?・・・というのが今日のお話。

さっそく先ほど1系統で行っていた処理を複数系統に拡張する事を考えてみます。下の図は、先ほどの処理を5並列で実行するようにしたものです。

上記の場合、スループット[MB/s] = ((32[MB] x 4 x 1) + (32[MB] x 3 x 4)) / 1[s] = 512[MB/s]となり、先ほどの128[MB/s]に対して大幅にスループットが向上している事がわかります。

直列的に処理する事になれた人が時間軸だけに注目してこの例を考えた場合、「250ミリ秒かかる処理なのに、どうして50ミリ秒毎に結果が出せるの?」と不思議に思うかもしれませんが、図示してみれば特別驚くべき事もありません。

このように、同じ処理を実行可能な系統を複数用意して並列動作させるだけで、システム全体のスループットを大幅に向上可能である事がわかります。上記の方法で重要な点のひとつは、系統毎の処理方法と処理時間は、系統を並列にする前の直列動作時と何ら変わりが無いという点です。

ここで、その他の方法として、単一の「ある処理」を複数に分割して、複数の系統で同時に並列処理する事も考えてみましょう。下記は、緑の部分は5つに分割して処理した結果、時間軸上では1/5の時間で済むことを示しています。


上記の場合、250ミリ秒かかる「ある処理」を5分割にして50ミリ秒ほどで完了する細かな処理に分割し、それら分割した処理を複数の系統で同時に処理する事で、レイテンシー50ミリ秒、計算系統5系統で「ある処理」を50ミリ秒で片づける事に設計する事になります。但し、並列化する前と後では、処理の実装が異なる他、並列化に要する新たなオーバーヘッドが発生する事があります。



システムにおけるレイテンシーとスループットを考える場合、何を並列化するのか、どのように並列化するのか、その並列化においてのスケーラビリティはどのようなものか、など様々な視点で設計について考察しなければなりません。

この話の続きには、同期と非同期や多段接続に関する話題、バッファリングに関する考察が必要ですが、それはまたの機会にと思います。

2015年9月30日水曜日

CQ出版社Interface 2015年11月号に「レベルアップ!フリーのCプログラミング道具箱」と題して記事を書きました

CQ出版社Interface 2015年11月号に「レベルアップ!フリーのCプログラミング道具箱」と題して、高品質ソフトウェア設計実装を目指す活動に関する記事を書きました。


実際の製品開発現場では、割り当てられた期間の中でどれだけ品質の高いものを素早く実現できるのかが鍵になってきます。現代の製品ではソフトウェアの設計実装活動の占める割合は高くなる一方ですが、十分に余裕のある形で品質を担保するための活動が出来ない事が少なからずあるようです。

また、現場によってはソフトウェアの設計や実装に関して着目する事が無く、とにかく製品全体を外部からのみ見て評価しようと試みるところもあるようです。しかし、実際に製品は内部の設計実装成果物によって動作しているわけですから、その内部品質について度外視する事は出来ません。
 このような背景から、今回の記事では、ソフトウェアの内部実装品質について現場レベルで活動するための道具を少しだけ紹介する内容としました。不安いっぱいの状態で製品をリリースするのは単なるリスクですし、顧客に迷惑をかける結果になりかねません。であれば、事前に色々な検証を手軽に楽しく行って、安心して製品をリリースする事でエンジニア生活を楽しもう!といった趣旨です。

図1. 設計と実装と内部検証と内部記録の関係

図1は、設計、実装、内部検証、内部記録の関係性を示したものです。設計から実装を行いますが、実装は内部検証とも関連があり、そして内部検証は設計とも関係がある・・・そんな関係性を接触面で表現したものになっています。

小さなマイコンで動作しているような組み込み装置、高性能なCPUで動作している広帯域サーバーなど、装置の規模や性格に依らず、様々な場面で使えるアプローチとしてコンパクトにまとめた記事になっていますので是非ご覧ください。

CQ出版社のウェブショップからも購入可能です。
http://shop.cqpub.co.jp/hanbai/booklist/series/Interface

2015年8月12日水曜日

約3年ぶりとなるNatural Tiny Shell (NT-Shell)の新しいバージョン0.3.0をリリースしました

数ヶ月前からやろうやろうと思っているだけで進んでいなかったNatural Tiny Shell (NT-Shell)の新しいバージョン0.3.0のリリースを実施しました。今回のバージョンからライブラリとサンプルのディレクトリを分離し、何がライブラリで何がサンプルなのか一目で理解できるようにしました。現在のところ、ACB-BF592のベアメタルなプログラムがサンプルとして追加してあります。

また、これを機にA tiny MML parserのリリース活動で得たヒントも踏まえてウェブサイトも少し新しくしました。今までのサイトは、何と言うか一応公開してありますという雰囲気で、何が何だかさっぱり意味分かりませんでした。


このブログを通じて幾つか改良のヒントも頂いているので、今後の開発の中で何らかの回答が出せるようにしていければ良いなぁと考えています。

2015年8月10日月曜日

Micro MML Player LPC812のMaker Faire Tokyo 2015出展報告

Maker Faire Tokyo 2015にMicro MML Player LPC812を出展しました。

ちょっと愉快にMMLを再生しながら、音符まで表示しちゃうギミックを追加。
展示をご覧頂いた方からは「小さい!」とか「可愛い!」とか「音が聞こえない!」とか「何の曲を演奏しているのかわからん!」とか、大好評な御意見を様々に頂きました。



LPC812は、ROMが16KB、RAMが4KBと32ビットプロセッサとしては小さな部類に入りますが、今回はこのプロセッサにリアルタイム・オペレーティング・システム(UOS-LPC800)と、MMLパーサー(A tiny MML parser)を搭載。MMLのパースと音符の表示系の処理をそれぞれタスクに分離する事で、重い描画系の処理と時間軸がズレては困る音声系の処理の並列動作を実現しています。
 本当はシェル(Natural Tiny Shell : NT-Shell)も載せたかったのですが、これは入りきらずに断念しました。え?手段と目的が滅茶苦茶になってるって?そうです。手段と目的がごった煮になってま・・・。


ブースの左側は今回メインのMicro MML Player LPC812を展示しました。

準備段階では「音が煩過ぎたらどうしよう」とか考えていたのですが、「何言ってんだい」という感じ。
ブースでは全く音が聞こえませんでした。「あぁ、音も出てるんですね!」という声が続発!ついでに「あぁ、音符も表示してるんですね!」って。結局何を展示しているのかわからん!という話だったようです。なるほど。



ブース右側には今回のシステムで動作しているオペレーティング・システムを展示しました。
これがまた目的と・・・手段を・・・ぐるぐる鍋でかき回して・・・。



こーんな感じの展示で、次回の展示募集でこの内容は通らないだろうな・・・と思いました。
来年は違う路線で行こう!
最後に今回のファームウェアのコールグラフ!
こんな小さなマイコンで複数のタスクが動作するなんて素敵だなぁ!
だから・・・そうじゃなくて・・・。

2015年7月31日金曜日

Maker Faire Tokyo 2015にMicro MML Player LPC812を出展します

Maker Faire Tokyo 2015に出展します。


 今回はA tiny MML parserを使った小型ガジェットを出展します。


その小型ガジェットはオペレーティングシステムにUOS-LPC800を採用しています。


その名もMicro MML Player LPC812です。


ミュージック・サウンドのエリアで出展しています。

2015年6月30日火曜日

Natural Tiny Shell (NT-Shell)の更新予定について

マイコンベースの小規模組み込みシステムでお手軽にシェルを実現するNatural Tiny Shell (NT-Shell)ですが、数ヶ月前から更新を予定していました。


というのも、数年前に公開したパッケージは、フラットにソースコードが格納されているのみで、正直言ってどうやって使うのかイメージできるようなものではありません。

当時はおそらく「まぁ、こんな感じでmain関数に組み込んでおけばわかるかな?」くらいにしか考えなかったのですが、今見てみると「これは考えていることが伝わらないよ・・・」という印象。

おまけに、詳しい説明は月刊Interfaceにしか掲載されておらず、そして、そのInterfaceの記事はNatural Tiny Monitor (NT-Monitor)がメインになっているような構成です。

現在、ライブラリのパッケージングを工夫して、コアライブラリファイルと、ユーティリティライブラリファイルを分離する方向で調整を進めています。(調整って言っても、ちょこちょこっとやるだけなんですが・・・)


加えて、幾つかのプラットフォーム向けのサンプルプログラムを同梱することで、具体的な活用イメージをお伝えしようとの考え。例えば、Blackfin向けのDSPのサンプルプログラムは、以下のような呼び出し関係を持ちます。


プラットフォームを初期化してNT-Shellを呼び出せば、もうばっちりシェルインターフェースを持つ立派な組み込みシステム!というパッケージにしようという考え。

やるやると言いながらなかなか公開していないNT-Shellですが、幾つかのバグフィックスも含める予定ですので、今しばらくお待ち頂ければ幸いです。

2015年5月31日日曜日

LPC810でも動作するリアルタイム・オペレーティング・システムのUOS-LPC800にタスク間通信機能を追加しました!

あらまし

約二年も前の話ですが、「割と適当に動作するOS「誰得OS」のCortex-M0+版であるUOS-LPC800を作りました」で 、LPC810でも動作するリアルタイム・オペレーティング・システムを作りました。LPC800シリーズの厳しい制約(ROM: 4KB、RAM: 1KB)の中でOSを動作させてみたいという欲求と、実際にこのような制約の中で何が出来るのか、単純に技術的な興味があったからです。

当初のバージョンにおいて、タスク側から操作可能なAPIはuos_task_yieldとuos_task_sleepのみで、OSと呼ぶならば欲しいであろうタスク間通信すら追加しませんでした。 サンプル・プログラムは、片方のタスクでLEDを点滅させ、もう片方のタスクでシリアル通信を扱うだけのものです。まさに「誰得OS」の名前に相応しいアプリケーション。


なかなか思い切った割り切りだと当時は考えましたが、先日から実アプリケーションにUOS-LPC800を適用してみようと考え始めた時点で、タスク間通信の必要性を改めて感じる事になりました。各タスクがそれぞれの仕事をこなしつつ、他のタスクと協調して動作させようと考えた場合、タスク間通信は必須とも言える機能です。 やっぱり欲しいよね・・・。

設計

タスク間通信を追加する前のUOS-LPC800におけるタスクの状態遷移は以下です。タスクは、基本的にRunningとReadyを行ったり来たりしており、uos_task_sleepによってスリープ状態に遷移するようにしました。非常にシンプルな作りです。


今回、タスク間通信を追加するにあたって、方針を「出来るだけ現状の設計には手を加えないで追加する」とし、スリープ処理に手を加えない形でタスク間通信を追加する事にしました。

今回のタスク間通信(メッセージ・パッシング)のモデルは以下のようなものです。 Task Aからuos_task_event_sendに対して「送信先タスクID番号」と「32ビットの値」を渡すと、あーら不思議、uos_task_event_recvを使って待ち状態に入っているTask Bに値が渡って実行状態に遷移するというものです。モデルは至って単純。


今回、上記の単純なモデルを実現するために、カーネル内部にブロック状態を管理するタスク・コンテキスト・ブロックのキューを新設し、送信待ちタスクと受信待ちタスクをブロック状態のキューに入れてから処理する事にしました。状態遷移は以下のようになります。


本当は、スリープ状態もブロック状態の一種として扱え、相当な理由が無い限り分離する意味はなさそうなのですが、今回は既存機能に手を加えない事にしたのでそのまま。もしかしたら次のアップデートではブロック状態をBlockedに変更して、現在のBlockとSleepと統合するかもしれません。

サンプル・アプリケーション

サンプル・アプリケーションは、従来通り二つのタスクを用意しました。タスク間通信を使用する場合は、task_ttyからtask_ledに向かってタスク間通信機能を使ってLEDの点灯指令を発行します。外から見た動作は従来のサンプルと何ら変わらないのですが、タスク間通信で指令するようになったところが従来と異なります。

ちなみに、タスク間通信を使う場合、直接タスク間通信の関数を呼び出しても良いのですが、受信側タスクにサービス要求用のAPIを作って、送信側のタスクがそのサービス要求用のAPIを呼び出す方が筋が良い設計です。


巷に溢れる数多くのリアルタイム・オペレーティング・システムを使った実装例には、受信側タスクの内部事情を送信側タスクが知らなければ実装出来ないような記述が多く見られます。これではタスク間通信によって得られるはずの抽象化度を上げる事によるメリットの多くを享受出来ません。受信側タスクの事情を変更した場合(例えば、送信側に期待する送信内容を変更したとか)に、送信側タスクの実装を修正しなければならないとしたら、規模の大きな設計ではたちまちバグになります。

ダウンロード

2015年5月24日日曜日

「Blackfin MiniConfig for IFX-49 | 音遊び!Blackfin DSP基板でディジタル信号処理初体験」のサポート情報ページを更新しました

Blackfin MiniConfig for IFX-49 | 音遊び!Blackfin DSP基板でディジタル信号処理初体験」のサポート情報ページを更新しました。

1つ目は「Makefileからminiconfig-cuiを呼び出す」で、書籍に付属しているソースコードに添付されているMakefileにwriteとbootというターゲットを追加し、make一発でコンパイルから書き込みまでを実現する方法をまとめたものです。修正済みのMakefileをまとめたパッケージもダウンロードできるようにしておきました。


2つ目は「Eclipseを使ったビルド環境構築」の続編とも言える「Eclipseからminiconfig-cuiを呼び出す」で、ビルド後に自動的にminiconfig-cui.exeを呼び出してターゲットに書き込みを実行できるというものです。アプリケーション間を行ったり来たりする事なく、Eclipseから1クリックで書き込みまで実行できるお勧めの設定を記述しました。フラッシュロムに書き込む方法とSRAM上で直接実行する方法の両方に対応しています。


2015年4月9日木曜日

CQ出版社から「音遊び!Blackfin DSP基板でディジタル信号処理初体験」が発売されました

■書籍について

CQ出版社から「音遊び!Blackfin DSP基板でディジタル信号処理初体験」が発売されました。


この書籍にはAnalog Devices社のDSP (ADSP-BF592)が搭載された基板が同梱されており、書籍内容に連動して様々なエフェクト処理を体験できる内容になっています。今回の基板はBOOT用のピンヘッダをひとつハンダ付けするだけで、すぐに使い始める事が出来る豪華仕様。従来の付属基板と違って、USBコネクタもオーディオ入出力コネクタも予めハンダ付けされているのです。部品を別途購入する必要が無いのでとても便利!

■書き込みソフトウェア

今回、私は付属基板(IFX-49)のフラッシュ書き込みソフトウェアの設計実装を担当しました。


ソフトウェアはWindows版とMac OS X版を用意し、ビルド済みのバイナリが同梱されています。ソフトウェアからは、使ってみたいエフェクトを選択して書き込み操作を実行するだけで、様々なエフェクトをすぐに体験出来るようにしました。



また、Windows版には書籍に連動してビルドと書き込みが出来るようにソースコードまで同梱しました。セットアップを実行するだけで本当にすぐに使い始められるようになっています。

■オフ会とフォローアップ情報

2015年5月22日(金)には、CQ出版社主催のオフ会も開催されます。
また、 http://cubeatsystems.com/ifx-49/ で様々なフォローアップ情報も公開する予定ですのでお楽しみに!


■関連リンク

2015年4月5日日曜日

新しいユーザーイベント機能が追加されたA tiny MML parserのVersion 0.5.0をリリースしました。(ついでにRaspberry Piのサンプルも追加しました)

先月の事ですが、Make: Japanさんの記事「Arduinoでちょっと長めの音楽を再生する方法」でA tiny MML parserを御紹介頂きました。Make: Japanさんありがとうございます。


記事を眺めていて「実世界と繋がる機能を加えれば、他の要素と組み合わせて動作するシステムを作れるよなぁ」と考えました。要するに「A tiny MML parserは単にシーケンサーエンジンである」と考えるならば(まぁ、まさにその通りですが・・・)、シーケンサーが出すイベントに応じて外界を制御するグル―ロジックを付け加えるだけで、様々なコンポーネントを制御するシステムになる・・・というわけです。

そこで、A tiny MML parser Version 0.5.0では、新しくシンプルなユーザーイベント機能を搭載する事にしました。このユーザーイベント機能は、MML文の中に@{MY_EVENT}のように@で始まり{}で括った文字列を書いておくと、ユーザーコールバック関数に「'MY_EVENT'というユーザーイベントだよ!」と通知をしてくれるというものです。


今回のユーザーイベント機能を使うと、例えば以下のような事が簡単に実現できます。
  • 曲の演奏に合わせてLEDを光らせる。
  • 曲の演奏に合わせてモーターを動かす。
  • 曲の演奏に合わせて音色を切り替える。
  • 曲なんて鳴らさずに、イベントシーケンサとして使用する。
  • その他色々。
以下に示すのは、イベント文字列に応じて、サーボを制御する実装例です。
加えて、シリアル通信にイベントを通知しています。


曲の演奏状態に合わせてサーボを動作させる事が出来れば、MMLパペットが作れますね。 :)


非常にシンプルな機能ですが、従来のバージョンではユーザーアプリケーション側で何らかのトリックを加えて対応する必要があり、ちょっと何かこのタイミングでという簡単なニーズを手軽に実現する手段がありませんでした。今回の新しいバージョンに加わったユーザーイベント機能によって、曲の進み具合に合わせて、何かしらの処理をしたいという時に、本当に簡単に処理できるようになりました。やったね。


あと、今回のパッケージからRaspberry Piのサンプルも同梱する事にしました。Physical Pin 15 http://pi.gadgetoid.com/pinout/pin15_gpio22 にピエゾスピーカーを取り付けると演奏を楽しめます。こちらのサンプル、実は改良が必要なのですが、これはまた後日・・・やるかどうかわかりませんが・・・。


ダウンロードは https://www.cubeatsystems.com/tinymml/download.html からどうぞ。

2015年3月7日土曜日

LPC810でスーパーマリオを演奏するためにMMLの繰り返し処理を追加する 〜設計アプローチ編〜

LPC810でもMMLでスーパーマリオを演奏させたい!
先日A tiny MML parserを使ったスーパーマリオ演奏アプリケーションを公開しました。

折角なので眠っているLPC810への移植を試みたところ、4KBのフラッシュ・ロムに収まらない事がわかりました。実装したスーパーマリオのMMLを眺めると繰り返し処理をべた書きしています。MMLの処理を削る事は有り得ないので、安直に曲データを圧縮する事を考えました。A tiny MML parserに繰り返し処理を追加する事で曲データを圧縮を可能にし、LPC810でもスーパーマリオを演奏出来るようにしようと考えたわけです。


そんなにスーパーマリオに拘っているわけではないのですが、8ビットのマイコンにやらせていた事を32ビットのマイコンに移植して「残念!」みたいな結果になるのが嫌だったわけです。それがたとえフラッシュ・ロムの容量が原因だったとしても!(いや、それは無理・・・なんですけど)

繰り返し処理の仕様を考える
ひとくちにMMLと言っても様々な仕様、様々な実装が存在します。
A tiny MML parserに追加する繰り返し処理の仕様を以下のように定めました。
  • []の記号を用いて繰り返し処理の区間を定義するものとする。
  • 繰り返し処理の区間はネストさせる事ができるものとする。
  • 繰り返し処理の区間は二回繰り返し処理されるものとする。
一見簡単そうに見えます。

乱暴なプログラマの場合、いきなり実装に入ってしまうかもしれません。
ちょっと考えて「出来るんじゃね?」と思う訳ですが、この記事では「簡単そうに見える処理が実際もそうなのか?」という視点でも見てみたいと思います。

最初にテストデータを定義する
A tiny MML parserに繰り返し処理を追加するにあたって、最初にテストデータを定義しました。このテストデータを新しいバージョンのA tiny MML parserに与えて、期待する結果が得られれば新たに加えられた処理は正しいという理路整然としたアプローチです。
  • [CDE]
  • [CDE[FGA]]
  • [[CDE]FGA]
  • [[]]
  • [C[D]E]
  • [CD[EF]GA]
上記のテストデータは正常系のテストデータです。異常系は以下のようにしました。
  • [[[[[[[[[[[[[[[[[[[[
  • ]]]]]]]]]]]]]]]]]]]]
  • [ ]]
  • [[ ]
  • ][
このようにちょっと単純すぎるテストデータですが、そもそもこの程度のテストデータを正しく処理出来ないとしたら論外です。そういう意味でも正常系と異常系のテストデータを簡単なもので良いから両方用意しておくというのは重要です。

安直な設計でどうなるのか考えてみる
それではここで、めちゃくちゃ安直に設計無しで実装に取りかかった場合、どのようになるのかシミュレーションしてみましょう。

新人A君の場合
新人A君は、学生時代にマイコンでプログラミングを楽しんでいた意欲的な新人です。
与えられた仕様に対して直感で「これは位置を記憶する変数とループ状態を保持するフラグがあれば解決出来る!」と意気込み、早速プログラミングに取りかかる事にしました。


新人A君は、とりあえず実装しながら動作を確認し、ちょこちょこ修正していくアプローチで、学生時代には沢山の作品(製品ではない事に注意)を作ってきました。今回の新人A君の設計は仕様を満たせるでしょうか?

新人A君は、ざざざーとプログラムを意気揚々と書き上げました。
彼の最初のバージョンは、以下のテストデータを正しく処理しました。
  • [CDE]
そして、以下のテストデータは正しく処理できませんでした。
  • [CDE[FGA]]
  • [[CDE]FGA]
  • [[]]
  • [C[D]E]
  • [CD[EF]GA]
つまり、殆ど正しく処理できなかったわけです。
新人A君は「ネストなんていらないですよ。」と仕様に文句を付け始めました。
これはいけません。

中堅B君の場合
中堅B君は、現場でXX年の経験を積んだ社員です。
最近は様々な案件の検討にも加わり、かなり自信が付いています。
課題に対する設計アプローチにも幅が広がり、色々なキーワードと共にアイデアが出せる人材です。


中堅B君はスタックを使った設計アイデアの妥当性を確認するために簡単なテストプログラムを作って事前検証する事にしました。

彼の最初のバージョンは、以下のテストデータを正しく処理しました。
  • [CDE]
そして、以下のテストデータは正しく処理できませんでした。
  • [CDE[FGA]]
  • [[CDE]FGA]
  • [[]]
  • [C[D]E]
  • [CD[EF]GA]
つまり、殆ど正しく処理できなかったわけです。
但し、新人A君と異なり設計にスタック構造を用いており、繰り返し開始括弧である[記号が複数やってくるところまでは、設計上でうまく対応できています。

新人A君と中堅B君
新人A君は、そもそも仕様を無視して何でも良いから動かそうとしています。
これでは何をやっているのかわかりません。

中堅B君は、スタック構造へ誘ったのは良かったのです。
が、ちょっと詰めが甘かったようです。

A tiny MML parserにおける設計
A tiny MML parserにおける設計は、以下のように行ないました。
  1. 簡単なスタックモジュールを設計する
  2. 代表的なパターンを定義する (これはテストデータの一部でもある)
  3. 定義したパターンを満たす一般ルールを見つける
  4. 一般ルールを他のパターンに適用して破綻がないか確認する
始めに簡単なスタックモジュールを設計しました。このモジュールがあまり大きいと、繰り返し処理で削減したはずのメモリを追加したスタックモジュールで食い潰す矛盾が起きてしまいます。スタックモジュールはMMLの操作とは完全に独立させ、単独で検証できるようにしました。



charへのポインタをプッシュ、ポップ出来る設計です。
このスタックモジュールの単独検証終了後、全体操作の設計を開始しました。

以下は、最初に完成した操作に関する設計メモです。


代表的なパターンを定義する事で、その次に続く一般ルールを見つける作業の前提が明らかになります。定義したパターンに対して矛盾無く処理可能な一般ルールを見つけ、それを他のパターンに適用してみます。他のパターンに適用して破綻が有る場合、それは一般ルールではなく、ある前提に依存したルールという事になります。

上記設計メモに書かれたルールは、突然箇条書きにして生まれたわけではありません。特定の代表的なパターンを用意し、二つのスタックが理想的にどのような内部状態であれば処理が出来ていると言えるのか?を考えて、それを一般ルールにしたものです。

例えば、以下のようなパターンを挙げ、Dの後ろにある終了括弧に着目します。


この終了括弧に到達した直後の理想的なスタック状態を列挙したのが以下。


後は上記のスタック状態になるような操作を一般ルールに落とし込み、その操作が派生パターンでも破綻しない事を確認します。上記の例で言うと、一番近い派生パターンから見るとして以下の3パターンでしょう。


で、LPC810には入り切った?
いいえ・・・。
予想以上にROM 4KBが厳しい制約!

どのくらい入っていないかと調べてみると、text合計が6,186[Byte]ですから約2KBほど溢れています。曲データが収められているのはmain.oで、この中に制御コードは殆ど含まれません。-Osを付けてコンパイルしたオブジェクトを、arm-none-eabi-size *.oで見た結果が以下。


今回追加したリピート処理によって、スーパーマリオの曲データは3,464[Byte]から2,175[Byte]へ約62.8%圧縮出来ましたが、その差はたったの1,289Byteでした。text合計の話と合わせると、ほぼ曲データ分だけ溢れている事がわかりました。上記を見る限り、MMLモジュールを全て取り除いたとしても入りません。そもそもMMLモジュールを取り除いては意味もありません。

あぁ、こういうアプリケーションは想定していなかったなぁという感じです。例えば、小さいプロセッサからEEPROMなどのデータを見に行くというものを考えた場合、現在のA tiny MML parserは、セットアップの時点でデータが確定している事を前提にしているので、逐次データを読みに行くような制御に対応できません。

なるほど、自分で使ってみるものだなぁと改めて思ってしまいます。
どうしようかな・・・。