2019年11月29日金曜日

教科書に載らないソフトウェア開発入門 (インターフェースを切る事と実現可能なソフトウェア規模についての話)

どうやったらもっとまともにソフトウェアを書くことができるようになるのか?

僕も若い頃、いや、まだ若いんだけど、本当にどうやってソフトウェアを書いてゆけば良いのかわからなくて、とにかくダラダラダラダラダラダラダラダラ「きっとやらなくてはいけない処理」を羅列したものだった。それも膨大な量のコードを。

・・・と書こうと思ったのだが、実は自分自身はそうではなかった。

以前書いた「アセンブラでこんなに美しく書ける方がいるんだ!」という純粋な感動体験が完全に邪魔をして、職業でソフトウェアを書く時に美しさに何かの美学を見出していた若かりし自分は、とにかくいかに美しく問題を解決するのか?についてひたすら毎日考えていた。このおかげで、人よりも余計な苦労をしなくてはならなかったのと、まだ若かった自分には設計や実装に関する知恵も知識もなかった上に余計なことに思慮を巡らせるものだからとにかく時間がかかっていた。本当にあれは今でも周囲にとっては単なる迷惑な新人だっただろうと想像できる。


新しくソフトウェアを書こうと思う人にとっての最初の疑問は、どうやったらもっとまともにソフトウェアを書くことができるようになるのか?ということかもしれない。少なくとも自分はそうだった。

インターフェースを切れるようになること

「どうやったらもっとまともにソフトウェアを書くことができるようになるのか?」という疑問に対する答えのひとつは「インターフェースを切れるようになること」だ。

「インターフェースを切る」というのは、つまり、対象物が何であるのか、というのと、それをどうするのか、という二つの事を分析して理解した上で、使う側と使われる側の双方の立場で物事を考えなければならない。この分析を経てようやく何かのインターフェースを策定できるようになる。


この分析から理解、理解から策定に至るまでの道筋を立てられるようになると、インターフェースを切れるようになってくるのだけど、インターフェースが切れるようになってくると、大きな設計対象物でもそれらを部分に分割し、適切な階層構造を構築できるようになる。適切な階層構造を構築できるようになると、全体も部分もそれぞれの粒度で理解できるようになる。そして、階層構造で設計対象物を見れるようになることでソフトウェアの全体構造を見る視点が養われる。つまり、この時点で規模の大きなソフトウェアを実現できるようになってくるんだ!

この段階に入ってくると、設計対象物を様々な見方で分析して理解できるようになってくる。データの流れで全体構造を見たり、論理的な階層構造から全体構造を見たりすると楽しいものなんだ。そして、何よりも大切なのは、楽しいと自分がやりたくなるっていうこと。苦労だけすれば良いってものではないからね。


つまり、インターフェースを切るということは、全体を部分に分割するのに必要不可欠で、分割することで大きな規模のソフトウェアも実現できる。それだけではなく、全体を部分に分割する中で、関係のあるものと関係のないものを分離することが可能になり、よりソフトウェアが洗練される。例えば、何かのソフトウェアを書いていて「この処理は他でも使えるな」という処理があったとする。そういう処理は一度書いておけばどんどん使えるネタとして持っておくことができるようになるんだ。

インターフェースを切っていると出来る芸当もあって、インターフェースを切っていることで、何かの変更が必要になった時にコンパイラに助けてもらうこともできる。

例えば、インターフェースに新しい機能を追加したくなったとする。インターフェースの定義を変更するだけで、そのインターフェースを使っている上位層をあぶりだすことができるんだ。だって、変更して形の変わったインターフェースを使おうとしている上位のコードは、必ずコンパイルエラーになるでしょ?

こんな風に、インターフェースを切るという話を始めただけでも、色々な側面の話が出てくるってこと。頭の片隅にあっても良いかもしれない。

2019年11月17日日曜日

教科書に載らないソフトウェア開発入門 (インターフェースの定義と実装の詳細)

インターフェースの定義と実装の詳細

「教科書に載らない」と書くからには、教科書に書かれないような(でも非常に大切なこと)ことを書こうと思う。

働き始めて直ぐに横にいた先輩は、とある業務用ビデオ装置のディスク制御ファームウェアを書いていた人だった。この先輩は日本語の使い方しかり、話し方しかり、色々と事細かに注文を付けてきた。入社したての自分にとっては煩いだけの先輩だったのだけど、今思うと非常に大切なことをここで学んだように思う。

このシリーズの冒頭では、同じことを指示しているような一つの単語があったとしても、見方や認識の差から結果的に全く異なる場所に行きついてしまう点について書いた。実はこの先輩も同じような事を言いたくて若い頃の自分に事細かに指導をしてくれていたのだと思う。

ここ約20年ほど、様々なソフトウェア開発者の設計や実装を見てきて、如実に実力の差が明らかになるのは、インターフェースの定義と実装の詳細についての明確な思想が育っているか、という点だ。経験の浅いソフトウェア開発者、あるいは経験はあるはずなのに一向に技術レベルが向上しないソフトウェア開発者には共通して「インターフェースの定義と実装の詳細を明確に区別できていない」という問題があった。

おそらくこの区別を最初に身に付けておくだけでも、相当なレベルの差になりそうだから、ここで非常に重要なこととして「インターフェースの定義と実装の詳細」について述べておくことにしたい。


もしかしたら、とても重要でかつ唯一といって良いかもしれない。そしたらこのシリーズは既に今日の内容で終わってしまうことになるが、それはそれで良いだろう。

インターフェースの定義

インターフェースとは、ある面と別のまたある面の境界にある出入口のことだ。ざっくり言うとね。その出入口にはそれぞれ定められた規則があって、その規則に従う限り何かを通すことができる。”何か”っていうのがポイントだ。この通すものや規則を具体的に定めるのがインターフェースの定義。この定義に従う限り面と面を接続できる。

これはソフトウェアの言語に依らない考え方ではあるのだけど、具体的な例が欲しいのでここではC言語で書いてみようと思う。

int getc(FILE *stream);

例えば、上記の関数があったとしよう。この関数はFILEへのポインタを第一引数に取り、intを返値に持つ。この関数の実際の動作はインターフェースを定義した際に具体的な挙動が決められることになる。この関数はlibcの標準ライブラリだが、実際にこの一見簡単に見えるインターフェースでさえ、インターフェースの定義が明確になっていない限り、どのような動作になるのか見当もつかないはずだ。

例に挙げると
  • streamにnullを与えるとどうなるの?
  • streamに与えるFILEへのポインタとは一体なんなの?
  • streamのFILEへのポインタは開かれていないFILEだったらどうなるの?
  • 返値のintはどういう規則で返されるものなの?
のように細かな動作を決めるのはインターフェースの定義に含まれる。いずれにせよ重要なのは外部から見た出入口の振る舞いを決めるということだ。

このインターフェースの定義は何もソフトウェアに依らない。ハードウェアでも同じように考えることができる。このチップはI2CバスとSPIバスの橋渡しをするデバイスだ。インターフェースにI2CバスとSPIバスを取っている。


実装の詳細

実装の詳細とは、インターフェースの定義で定められた振る舞いを実現するためのものだ。とても乱暴に言うと、インターフェースの定義で定められた振る舞いさえ提供できれば、中身の実装がどんな風になっていても構わない。もちろん実装詳細の具体的な内容は時と場合によって正解もあり間違いも当然ながら存在するが。

例えば、二つの数を与えると加算して返す関数があったとする。インターフェースの裏側にある実装の詳細がコンピュータで実現されていても良いし、小人さんが10人で寄ってたかって一生懸命計算していても良いし、紙テープで何か不思議な計算処理をしていても良い。つまり、インターフェースの定義に従っていさえすれば良い、というのが実装の詳細の基本的な考えだ。

ここまでで

ここまででインターフェースの定義と実装の詳細について簡単に説明した。具体的なイメージは上記の説明だけでは不十分で、これから先のシリーズでより具体的な設計や実装のイメージをつかめるようにしていきたいと思う。

2019年11月8日金曜日

教科書に載らないソフトウェア開発入門 (今はあまり出来ないと考えている君へ)

今できることと将来の自分

「教科書に載らないソフトウェア開発入門」なのだから、当然のように最初からソフトウェア開発の何たるかについてツラツラと書き記したい気持ちがあるのだが、やっぱり先の記事と同様に少しだけ寄り道をしようと思う。

というのも、15年ほどソフトウェア開発をしてきて振り返ると、今でこそ考えた通りに動作するソフトウェアが出来るようになったものの、始めた一年目なんかは本当にひどい状況だった。書くソフトウェアの全てが100%の確率で思った通りに動かない。動かないならまだしも、そもそもコンパイルも通らないしリンクとか言われてもワケがわからない。

もう本当に詰まらないというか何をやっているのか自分でわからないような日々が続いた。そもそも自分がソフトウェア開発をやることになったのは、自分で設計した基板に載せていたマイクロコントローラのファームウェアをアセンブラでスラスラと書いているのが間違ってソフトウェア開発を統括する人の目に留まってしまったからだ。これは失敗だったと当時は何度も思った。

そんな動きもしないソフトウェアを何年も作り続ける日が続いたのを今では懐かしく思うが、当時は本当にこれは駄目だなと何度も思った。手掛かりになればと色々な書籍やウェブを読み漁ったけど、どれもこれもピンと来ない。当時はなぜなのかわからなかった。

何かのきっかけ

物事にはきっかけというものがあるみたいだけど、自分のソフトウェア開発の状況を徐々に改善するきっかけになったのは、やっぱりこれも「どうしようもなく動かないソフトウェア」を作ってしまった時だった。もう本当に納品することになっていたソフトウェアを担当していた自分は、先輩から言われた前提条件に従って限定的に動作するソフトウェアを書いていた。この前提条件に従う限り、確かにソフトウェアは動作した。まずかったのは、その前提条件があまりにも厳しく、殆どの場合で適用できなかったことだ。

そんなソフトウェアを納品に持って行った結果、御客様の落胆度合いは半端でなかった。また、不運にも、というか当然の結果として御客様の環境はその前提条件に該当しなかった。ソフトウェアについての説明を加えながら実際に動作をさせていったところ、あらゆる処理で例外が発生した。これには本当に作っていた本人が参ってしまった。当然ながら御客様はそれ以上に参ってしまった。今でも表情を思い出せるくらいの落胆さを示された御客様は、お引き取り下さいだったか何かを仰っていたように思うが、もう当時のこの瞬間を思い出すことも出来ない。記憶から消してしまったのだろう。

とにかく、この厳しい前提条件で限定的に動作するソフトウェアは、本当に世の中に何の役にも立たず、もっと言うと様々な人に害を与えるために世に生まれてきてしまったのだった。

この話を聞きつけた上司(彼がその厳しい前提条件で良いと話していた)は、顧客と同様に激しく落胆し、私を担当から外して別の人間に修理させようといった内容の話をしていたようだ。その話を聞いた自分は落胆に落胆を重ねた落胆パレードに参加しているかのような錯覚に陥った。ひどく参って次の日は一日休んで何かを考える日に使った。


心に決めたこと

休んだ一日は本当に何をやっても手に付かなかったし、自分のボロボロのソフトウェアを修正する担当を逆恨みしたりした。これはひどい。出来ない人の典型みたいだ。だから決めた。ちゃんと作ろうと。ちゃんと作るのは当時の自分からすると何をして良いのかわからなかった。唯一わかっていたのは、動かないソフトウェアは「考えられていない」ということ。

何を考えるのかも重要だったと思うけど、とにかくあちこち隅々まで考えられていない事で徹底されていた。当時はわからなかったけど、厳しい前提条件を自分の思い込みで設定してしまった上司が典型例だった。特に何も考えないで「それで良いんじゃない?」と言う。その何かにとって都合の良い思い込みで作り続けてしまう。そんな事はもうたくさんだと思った。

その日からとにかく考えて作ることにした。まず、厳しい前提条件は何故生まれてしまったのか?その前提条件を外す方法は何か?そもそも前提条件を外すのは無理なのか?なぜ人々は過ちを犯してしまうのか。(いや、それは考えすぎだった)

調べてみるとその前提条件は、当時制御対象になっていた装置の実装制約から来ているものだった。とある信号を生成するのに使っていた設定ファイルが、無数の数式から出てきた値を保存するものになっていた。その値を編集するソフトウェアだったのだが、元の式が一体どういうものだったのかわからないものもあった。

仕方がないから100個くらいあった設定値の数式をあらゆるソースコードやユーティリティから漁っていくことにした。式は軽く数十個。中にはよくわからないパラメータも無数にあった。わからなくなってくると周りの先輩に聞いて回った。中には「そんなの勝手にやれ!」とか「なんでそんなの必要なんだよ!」と怒り出す先輩もいたが気にしなかった。「自分はこれを完成させないといけないんです」と逆切れした。

兎にも角にも「前提条件を外せば今よりももっとまともに動くはず」という目論みで進めた結果、意味不明な設定値の羅列だったものが、設定ファイルから元の数式にはまるパラメータを逆算できるようになり、これがきっかけで問題だらけだったソフトウェアが動作するようになった。

それだけでなく、パラメータの生まれた背景や意図を知る事になり、結果的に今まで出来なかったような便利な設定も出来るようになった。この結果を喜ぶ人は社内に殆どいなかったが、少なくとも御客様とその製品を主に担当していた先輩だけは喜んでくれた。

このことがきっかけになって、自分は他の人よりも考えることにしようと心に決めた。自分は頭が良い方ではない。それでも何かをしないといけないと思った。

みっともない話をする事について

こういうみっともない話(個人的な情けない話)は、ふつうはしないものだと思う。色んな人が色んな事を言うわけだし、そういう批判や非難は誰しも耳にしたくない。でも心に留めておいて欲しい。ソフトウェアを書くというのは、ありのままの自分でいないと書けないということ。わかる範囲でしか書けない。残念ながら現代の計算機は、未だもって思った通りには動かない。書いたとおりに動く。だから、自分の書いている範囲でしか動かない。

最近見た光景だが、その彼は様々な状況や人間にありがちな見栄から、どんな事を言われても「簡単です」と答えていた。実際にそれらがどうなったか。簡単と言われていた内容が、とてつもない人数をかけてやる一大プロジェクトだったり、熟練した人なら特に問題もなく完成させられるはずの内容を、いつまで経っても完成させられない状況に陥っていた。

彼は見栄っ張りだった。見栄やプライドは成長を阻害する。わからないものはわからない、難しそうなものは難しそう。それは何の恥でも無知でもない。それは個人の能力の限界かもしれないが、それは別に人格とも関係が無い。知らないものが事があるのは恥ではない。全てを知るのは無理だ。ただ素直に自分に対してそれを認めれば良い。馬鹿にする人も気にしなくて良い。そういう人達は単に暇なんだ。本当に。

今日もソフトウェア開発に直接関係なさそうな事を書いてしまったけど、もしかしたら一番大事なことなのかもしれない。

2019年11月3日日曜日

教科書に載らないソフトウェア開発入門 (はじめに)

万物世界共通

思い立って何かソフトウェア開発にまつわる話を書こうと思ったのだけど、おもむろに重い話を書くと読む人も嫌になると思ったので、いっけん関係ない話をしようと思う。

  「どんな物事も基本が一番簡単に見えて一番難しい」

例えば、ピアノを弾けない人に「明日からピアノでジャズを弾いてくれ」と頼んだとする。 面を食らった人はピアノ曲集みたいなのを手に入れておもむろに曲を弾き始めるだろう。 そうするとどうだろう。いかにも「曲集を練習しました」という演奏にしかならない。

長年この問題を考えていたけど、結局のところ動作や結果、それぞれひとつひとつには「背景となる思想や積み重ねがあってそうなる」という事かもしれない。少しニュアンスが難しい。もしかしたら何を言っているのかわからないかもしれない。

少し前の話、ビーバップの伝統を受け継ぐピアニスト、バリーハリス氏が教育の現場で学生に「Cmを弾いてくれ」と言った光景を見たことがある。たいていの学生はどうしてか弾きなれている(?)Cm7を弾いてしまう。つまり、特に何も考えることなく7thを足してしまうわけだ。こうなると話は全然違う方向になる。

バリー氏はCmを弾いて欲しいと頼んでいるにも関わらず、Cm7を弾く人達について、頭を使っていない(=Cm7はF7を経由してBbメジャーへのトニック進行を暗に示唆する)と見ているのだ。Cm7を弾いた時点で、結果的に本来の意図であるマイナーとは全く異なるメジャーに帰着することになり、異なる結果になってしまう。

つまり、そういうことだ。(え?)

すべての一見簡単に見えることが、あらゆる浅はかさによって全く異なる結果を生んでしまう。これが「どんな物事も基本が一番簡単に見えて一番難しい」という由縁だ。コードを1つ弾くだけでも間違いは起こり得るのだ。


ちょっと古い2000年頃の話

当時工学部の学生だった自分は、そろそろ真面目に勉強をして大学生活から外へ出たい気持ちになっていた。 電気好きの一級建築士だった祖父から受け継いだ電気への興味をそのままに電気電子工学科なる学科に進んだのは良いが、真空蒸着なんかをやっている間に何をやりたいのだっけ?とわからなくなっていた。

在学五年目に差し掛かったあたりから、当時徐々に日本国内で人気の出ていたマイクロコントローラに興味を持つようになり、研究室の先生にお願いして幾つかの部品を購入してもらった。正直に言おう。当時、手にしたものの中にはTI社のDSPもあったのだが、これはのっけから開発環境のセットアップがよくわからずに躓いて放っておいた。ごめん。あれは高かった。

まぁ、それはさておき、当時自分が触っていたマイクロコントローラは、8ビットのハーバードアーキテクチャを持つもので、プログラム上で変数を扱おうと思うと数少ないレジスタとメモリを駆使しながらアセンブラで組み上げるという代物だった。 当時はそもそもソフトウェア開発を生業にすることを考えていなかったので、ハードウェアを設計した後に付いてくるお愉しみみたいなものに過ぎなかったのをよく覚えている。

ところで、その当時の国内では「このマイクロコントローラといえばこの人!」みたいな著名な方がいて、沢山の書籍が出ていたのだが、ソフトウェア開発を知らない自分から見ても「この実装は質が低くないか?」と疑いの目でしか見れなくなっていた。


元々、「出来る人の言う事しか聞かない」ポリシーがあるので、この著名な方の書いてあることを信用するのはやめて、もっと他の良い教師を探すことにした。 時は2000年、ちょうどWindows NTやらWindows 2000やらが出て、世間はインターネットだとかウェブだとか色々とにぎやかになり始めたことだ。 研究室にあったSONYのノートパソコン(そうVAIOだ!)を勝手に拝借して自分の研究用にあてた上で、授業が終わるとウェブで必死に技術資料をかき集めた。

当時、この類の情報を国内で発信していた人はそれほど多くなかったとは思うが、その中に一つ「これは!」と思う実装を公開している人がいた。 具体的な名前は書かないけど、この人の実装を見た時から自分の本当のソフトウェア開発がスタートしたといっても過言ではない。 (いや、本当は中学生の頃からソフトウェアは書いていたのだけど、それは忘れて良いと思う。N88-BASICだし。)

ひとつ、良いことを教えよう。 その人はとても良い公開方法を選んでいた。 「人に考えさせること」だ。 当時その人が公開していたウェブには確かにソースコードがあった。 でも、実はそのままではコンパイルできないように、ある関数だけは非公開になっていた。 もうどんなセリフだったのか忘れてしまったけど「興味のある人は連絡をください」だったかな。 こんなに綺麗なソースコードをアセンブラでも書けるなんて素敵だと感動した自分はさっそくメールを打った。 ほどなくして返答を頂いた自分は「本当に宝物を頂いた」と感動して、すぐさまコードを印刷して一行ずつ穴があくまで読んだ。


で?

もう既にあれから20年くらい経って少し大人になってしまったのだけど、それからまだ自分はソフトウェアを書いている。 自分に感動を与えてくれたコードを書いた人にいまだ持って会えていないのだけど、そろそろ会うのではないかと考え始めている。 そして、自分もそろそろ先代がしてくれたことと同じように誰かに何かを与えて生きて行く段階に入っている。 そろそろ時間も足りなくなってくるので、その活動を始めようと思うのだ。

2017年11月30日木曜日

FreeRTOSがAWSオープンソースプロジェクトになってMITライセンスが採用されたらしい

最近はマイコンで遊ぶ暇もないくらい忙しいのですが、なんとFreeRTOSがAWSオープンソースプロジェクトになって、しかもMITライセンスが採用されたらしいです。

あぁ、たまにはゆっくりマイコンで遊びたいなぁ。

どんどん時代が変わっていきますね。
https://aws.amazon.com/jp/freertos/

2017年10月31日火曜日

ARMv6-M Architecture Reference Manual

先日の続きでARMv6-M Architecture Reference Manual(https://static.docs.arm.com/ddi0419/d/DDI0419D_armv6m_arm.pdf)を見ていきます。文書をざざざっと見渡し、まずはARM core registersの確認から進めます。D7.1にARMv6-Mにおけるコアレジスタ定義が表になっているのでここから進めることにしましょう。
続きは次回...え?

2017年9月30日土曜日

ARMv6-Mと戯れる 第1号 ~ARMv6-Mと戯れる準備をしよう~

まえがき

大抵の場合「ARMマイコン!ARMマイコン!」と言っているその中身は、ARM社が提供しているプロセッサに加えて、チップベンダー各社が周辺回路を加えてパッケージングされたものだったりします。 「マイコンを使えます」という人でも、自分が使っているマイコンがどういったプロセッサを使用しているのか詳細を答えられる人は稀で、せいぜい「Cortex-M0+です」とかその程度のものでしょう。

4年前の2013年、LPC810でも動作するUOS-LPC800を設計し、その過程でARMv6-Mのレジスタセットについて学習しました。 この学習過程を振り返った上で、再度見直して楽しんでみようというのが本シリーズです。

ブート!

学習過程を振り返るというお題があるので、学習を始める過程も挙げておきます。 まずは題材となるマイクロコントローラのデータシートを見ます。

NXP社のウェブよりLPC81X_LPC83X: Low-Cost Microcontrollers (MCUs) based on ARM® Cortex®-M0+ Coresには、ARM Cortex-M0+と書かれていますね。でも、この段階では「あぁ、ARM Cortex-M0+っていうのを使っているんだ。」程度にしかわかりません。

次に「このARM Cortex-M0+って何だ?」というのは、ARM社の情報を見る事になります。 https://developer.arm.com/products/processors/cortex-m/cortex-m0-plusには、ARM Cortex-M0+という絵の中に「CPU ARMv6-M」とあり、「あぁ、ARMv6-Mと呼ばれるCPUを使っているんだなぁ」と先ほどのARM Cortex-M0+から一段掘り下げた情報が得られます。

で、ハイライトを見ると、ISA Supportの欄に「Thumb/Thumb-2 subset.」と書かれています。この「ISA」というのは、Instruction Set Architectureの略で、命令セットアーキテクチャは「Thumb/Thumb-2のサブセットだよ」と言っています。

ここまでで、「ARM Cortex-M0+は、ARMv6-Mと呼ばれるCPUを使っていて、命令セットアーキテクチャはThumb/Thumb-2のサブセットである。」という事がわかりました。

さて、プロセッサと戯れるためには、ここで止まってはいけません。 更にhttps://developer.arm.com/products/architectureから、M-Profile Architecturesの情報https://developer.arm.com/products/architecture/m-profileに辿り着きます。

概要ページにはARMv6-Mアーキテクチャの概要も書かれており、「T32命令セットをサポート」と書かれています。 新しいキーワードT32命令セットが出てきましたね。

Instruction Setsのページhttps://developer.arm.com/products/architecture/instruction-setsを見ると、A64、A32、T32の各命令セットについてリンクが張られています。

https://developer.arm.com/products/architecture/instruction-sets/a32-and-t32-instruction-setsには「T32命令セットはARMv8アーキテクチャ以前にThumbとして知られていたもの」と書かれています。つまり、先に出てきたThumbと呼ばれる命令セットはT32命令セットである事がわかりました。

今回のまとめ

「ARM Cortex-M0+は、ARMv6-Mと呼ばれるCPUを使っていて、命令セットアーキテクチャはThumb/Thumb-2のサブセットである。T32命令セットはThumbとして知られている。」という事がわかりました。

次回は、ドキュメントのページhttps://developer.arm.com/products/architecture/m-profile/docsに辿り着いて色々と見てみましょう。

http://docs-api-peg.northeurope.cloudapp.azure.com/assets/ddi0419/c/DDI0419C_arm_architecture_v6m_reference_manual.pdfがアーキテクチャのリファレンスマニュアルです。