2012年12月9日日曜日

USB接続が可能な音源デバイスの自作

前置き

そういえば前回書き忘れましたが、今回の記事と前回の記事は、Web Music Developers JP Advent Calendar 2012に向けて書かれました。

デバイス自作のための準備

インタフェースの検討

全然ウェブとは関係ないですが、今回はちょっとテーマを広げて音源デバイスの自作について書きます。自作デバイスで全てを完結させることは困難ですので、どうしても外部から制御するためのインタフェースを載せる必要が出てきます。ハード工作的にはMIDIの口を作ることは簡単なのですが、やり取りできる情報は基本MIDI準拠の情報に限定されますので、MIDI⇔デバイスの間でプロトコル変換してあげる必要があり、それはそれで(デバッグが)面倒です。そこで今更ですがUSBの登場です。USBは利用する分には便利なのですが、いざ自作しようとすると仕様も大きく躊躇してしまいがちですが、簡単な制御だけなら比較的簡単に実装できます。また、USBを自作した時に問題になるのがドライバですが、将来的にはUSB APIを使ってブラウザから制御できるようになるんじゃないかなぁ・・・なんて楽観的に考えてます。ちなみにUSB APIはドライバを書く時と同じようなレイヤーのAPIになっています。(注:私もまだUSB APIは試してみた事がありません。gdkさんともお話をした事はなく、あくまで個人的かつ客観的な希望です。)

USBインタフェースの選択

電子工作レベルでUSBインタフェースを作ろうと思うと、だいたい選択肢は2つです。1つは今回このあと紹介するデバイスでも利用しているPIC18F2550を利用する方法。もう1つはAVRを利用する方法です。
PIC18F2550は秋月で350円で入手可能、かつ下の方に貼ったように書籍もでていますので、比較的敷居は低いのではないかと思います。また筆者さんがウェブでも参考記事を書いています。ただ、普通に開発しようとするとベンダー提供のUSBフレームワークを使う必要があり、標準開発環境MPLABが必須になります。MPLABはWindows版しかなく・・・と書こうと思ったんですが、MPLAB XになってWindows/Mac/Linuxが網羅されてるようです。この辺に日本語で記事が書かれてますが、普通にPIC18F2550の開発ができるようです。自分が試した頃は無償だと制限が多くWindows縛りもあったため、SDCCというオープンソースのCコンパイラ向けに、スクラッチでUSB関連のレジスタを叩くコードを書きました。あまり意味がなくなってしまったかもしれませんが、あとでこの辺も軽く紹介します。
AVRを使う方法がちょっとトリッキーで、V-USBというソフトウェアベースのUSBインタフェースを使います。これはUSBのD+/D-の信号をマイコンで直接読み書きしてくれるライブラリです。物理層の信号をマイコンが作るため1.5Mbpsの速度で汎用I/Oを0/1でパタパタさせます。マイコンのクロックが12MHz程度である事を考えると、ほぼ常時0/1の制御をしている事になります。よって、自作デバイスの制御にある程度CPUパワーが必要な場合には向きません。あるいはマイコン1つUSBインタフェースに専念させて、別のマイコンとシリアル接続、なんて使い方になります。こちらの方法については以前サンプル付きで詳しく書きましたので、そちらを参照して下さい。100円で手に入るTINY2313が使えるのが魅力です。また開発環境もgccなので安心・・・な人にとっては安心。人によっては不安の種になるかもしれませんが。
ちなみにPICの場合はハードウェアがUSBの信号をハンドルしてくれるので、ソフトウェアはデバイス設定やプロトコルの送受信をデバイスのレジスタ経由で指示するだけですみます。またUSB 2.0に完全対応しています。一方でAVRを使う場合にはUSB 1.1互換でかつlow-speedモードのみ利用できます。USB規格はlow-speedモードでのバルク転送、アイソクロナス転送を認めていないため、それらを使いたい場合には利用できません。

音源デバイス

音源デバイスを自作したい場合、大きく分けて2つのモチベーションがあるかと思います。1つは既存のデバイスをPCに繋げるようにしたい、というケース、もう1つ完全オリジナルの音源をスクラッチから開発するとうケースです。
私の場合は前者のケース。PSGやFM音源など往年の音源チップを入手し、PCから操作できるようにする、という物です。類似ケースとして、MIDI対応以前のビンテージシンセの接続なんかも考えられるでしょうか(そういっやシンセの場合、名器と呼ばれるものなら必ずMIDI化改造が出まわってますけどね)。
後者の場合はマイコンで簡易音源を自作したり、FPGAで本格的な音源を自作したりと言った感じですね。以前AVRで実験してた事がありますが、20MHzあればC言語で書いてもPSGくらいはエミュレーションできますし、アセンブラで書けばPSG+SCCやFM音源も十分にいけます。FPGAで自作する場合にはUSB回路自体をFPGAに入れてしまうのも手でしょうか。OpenCoresに使えるIPが幾つかあります。

製作例

PSG音源

秋月でも入手できる、いわゆるPSG音源互換のチップとしてYMZ294があります。コイツを使って作ったデバイスがこれ。作りが適当なのはご愛嬌。
写真左側のボードがUSB部、右側のボードがPSG音源部になります。

USB部の拡大写真です。右下の端子が汎用I/Oに接続され、別ボードの音源と繋げるようになってます。あと、テスト時に抜き差しで端子が弱ると嫌なので、電源スイッチをつけてます。それ以外はだいたいデータシートに載ってるリファレンス回路のまま。1000円もかからずに作れます。
こちらがPSG音源部。音源についてる4MHzクロックを使えば楽なのですが、ここでは敢えて家庭ゲーム機やMSXなどで使われていた3.58MHzを入力しています。またYMZ294の右側にPIC12F629が乗っているのですが、これは何か仕事をしているわけではなく、単にクロックを発振させるためだけに使っています。昔ながらのやり方だったら水晶の周りに発振回路を組むわけですが、面倒ですし。最近のマイコンだったらたいてい内部に色々な発振回路を持っていて、適切にfuse設定を書き込んでやれば発振後のクロックを出力してくれます。部品点数を減らして価格を抑えるって意味では海外のマイコンは非常に便利にできています。

SID音源

USB部はPSG音源と共通になっていますのでSID音源部のみの紹介です。
左の写真がSID音源部のボードになります。ところで、みなさんSID音源ってご存知でしょうか? Commodore 64に搭載されていた音源チップで、同時期の音源の中ではずば抜けて多機能な音源です。MOS Technologyの6581と呼ばれる物が初期の音源で、後期には機能追加された8580と呼ばれる物が流通しています。日本ではなかなか入手できず、海外のオークションで売られているものも故障品が多く、良品入手には非常に苦労しました。S.F.Pageさんのサイトに調度良い記事があったので気になる人は読んでみて下さい。一言で言えばワンチップ・ぷちアナログシンセ。萌える。
PSGと比べてそんなに差がないように見えるかもしれませんが、背面の配線は倍以上あります。なので下に見えるのは74374でラッチ。PICとの間のインタフェースのピン数の都合上、アドレスとデータを別サイクルに分けて転送してます。
さらに右の写真でアップした部分はSID音源ならではの苦労部分、MAX662による変圧回路。あと、ちょろっと見えてる黄色いコンデンサ。上の写真では6581とラッチの間に見える2つの黄色い部品です。実はこれらがSIDがアナログシンセたる所以です。実は6581は2つの電源を必要とします。デジタル部は5Vなので特に苦労はないのですが、アナログ部に使う電圧が12Vとちょっと、というかかなり高めなのです。さらに先ほど述べたコンデンサはアナログフィルタの特性を決める素子になっており、このコンデンサ如何により音色が変わってしまいます。って事で、部品調達にもちょっと手間とお金がかかりました。あと、もう1つ致命的なのが消費電力。実は6581はPMOSです。CMOSじゃないんです。なので消費電力は高めで、電源を入れるとあっという間に驚くほど熱くなります。ほんと最新のCPUバリの発熱。0.6〜1.0Wの消費電力となっており、実は変圧器の変換効率を考えると、2枚のボード合わせてUSBバスパワーの2.5Wを守っているのか怪しい・・・。

資料など

USB-PSG

地味ですが動画など。最初に一瞬出てくるコンソールはUSBモジュールのデバッグ用シリアル出力です。説明しませんでしたがUSBモジュールについてる右上の4本ピンがシリアルになってます。Windowsから思ってもいない初期化シーケンスが飛んできたりして、初期は結構はまりました。2つ目に出てくるコンソールはUSB接続先のLinuxマシンで、拙作のksgplayをUSB-PSG向けに修正したものを使い楽曲データを流し込んでます。KSGってのはMSX用の楽曲ログフォーマットで、PSGやOPLLの実装をチェックするために昔仕事で作った簡易プレイヤーがksgplayです。まぁ、実体は簡易MSXエミュレータです。YMZ294の横にあった2本品が出力になっていて、画面に見えてる小さい丸いスピーカーに繋いで音を出してます。
本当に動いてるの?って聞かれると、レジスタアクセスランプのLEDが光ってるでしょ、くらいしか証拠がない。

USB-SID


こちらはsidplay2という、本来は音源をエミュレーションして再生するオープンソースのプレイヤーを改造して実機対応にしています。最初の2つのチップは壊れていて、これは3つ目に入手したチップだったのですが、どうもch.1の音量が小さくて・・・やはりチップが完全な状態じゃないらしい。もちろん自分の回路がおかしくてチップを壊している可能性も否定できないわけですが。
こちらはアクセスランプすらないので、すでに実機を映すことすら諦めました。

PIC18F2550 USB制御コード

SDCC向けに書いたUSB制御コード。完全とは程遠いかもしれませんが、基本的な部分は問題なく動いてます。同じような事をしたい人の参考になるかもしれないので、一応Google Codeに当時のコードをアップしておきました。なんかもう、ファイル名がtest.cですよ。少なくともWindows Vistaと当時のLinuxのUSB初期化シーケンスには耐えてました。WindowsのUSBスタックも、だんだん厳しく仕様準拠を要求してきてるらしくて、もしかしたら7や8では動かない可能性もなきにしもあらず。その時は足りない部分をきちんと実装してあげて下さい。

参考書籍

改訂版が出ているらしいので、そちらをリンク。ちなみに自分は改定前の古い方を持ってます。MPLABからMPLAB Xになって色々と変わってるかもしれないので、使うツールに合わせて本を選んだほうが良いかも。

2012年12月2日日曜日

WebMidiLinkで遊んでみた

概要

ブラウザ上で動作するソフトウェア音源をボチボチ見かけるようになりました。g200kgさんの発案したWebMidiLinkは、こういった音源をJavaScriptから制御するための仕様です。あまり難しいことは考えずに手軽に利用できるのが嬉しいところです。
ということで、拙作のJavaScript向け音楽ライブラリをWebMidiLinkに対応させて、SMFを再生してみました。

おさらい

WebMidiLinkの話に入る前に、いくつか関連技術について説明しておきます。


Web Audio API

JavaScriptからオーディオを制御するために導入されたAPIです。W3CでGoogleのChris Rogersを中心に仕様の検討が進められています。現在利用できるブラウザはChromeとSafari 6のみですが、Firefoxも現在着々と実装を進めています。g200kgさんが指摘しているように、ChromeとSafariで若干動作が異なる部分もありますが、基本的には実装をWebKitの中で共有しており、ChromeとSafariでは製品で利用しているWebKitのバージョンが異なるのが主な原因と思われます。
今までもオーディオを制御する事はできたじゃないか、と思われる方もいるかもしれませんが、この仕様の(個人的に)一番嬉しいところは、JavaScriptで直接波形をリアルタイム生成できるところです。つまり、JavaScriptでシンセサイザーを実装したりできるわけです。また、オシレータやフィルタ、ディレイといった基本的な音響処理がコンポーネントとして提供されており、これらを接続して音響プログラムを楽しむこともできます。このあたりはEijiさんのはじめてのWeb Audio APIが参考になると思います。

Web MIDI API

同様にMIDIを制御するために考案中のAPIです。同じくW3Cで仕様が議論されていますが、今のところ実装が存在しません。Editorの1人Chris Wilsonが、最近W3Cのpublic-audioの場でWebKit上で実装を進める意思のある旨を表明しました。
WebMidiLinkはこの仕様とは独立したアイデアで、今すぐに利用できるAPIでもあります。

WebMibiLink

WebMidiLinkについて簡単な仕様の説明とデモを紹介します。

仕様について

g200kgさんが考案した仕様で、Web Audio APIなどを使って作られたブラウザ上で動作するシンセサイザーを接続するための規格です。MIDIを基本としており、この仕様ではMIDI信号をどうやってJavaScript上で表現し、コンポーネント間でやりとりするかが規定されています。g200kgさんの仕様ページが、端的かつ具体的なサンプル付きで非常にわかりやすいため、仕様の具体的な説明は割愛します。
ところで、ソフトウェアで音源を実装する場合、一定サイズのバッファ単位で波形合成を行うため、波形合成のタイミングと再生されるタイミングの時間差は一定になりません。別の言い方をすると、波形合成が1秒ごとに行われた場合は、外部からのMIDI入力も1秒ごとに反映されることになります。この問題を解決するためには、MIDI信号に時間情報を載せてあげるか、ソフトウェア音源の波形合成をなるべく小さい単位で実施してあげる必要があります。WebMidiLinkでは特に時間をケアしていませんので、音源はなるべく小さい単位で合成しないと、カクカクの演奏になってしまいます。
今回試してみた範囲では、時間方向の精度はほぼ気にならないレベルでした。

SMF再生デモ

下の黒い領域にSMF(MID)ファイルを放り込んで下さい。下の枠に表示されているWebMidiLink対応音源で再生されます。利用する音源はドロップダウンメニューから選択できます。標準ではg200kgさんのGMPlayerがロードされています。
* * * * * * * * * * * * * * * *
DRAG SMF FILE TO HERE






デモの説明

デモを見て技術的な部分に興味を持った方、ぜひ以下を読んでみて下さい。

ライブラリについて

T'SoundSystemという自前ライブラリのJavaScript版に追加機能としてSMF再生やWebMidiLinkを実装しました。元々はネイティブ実装されてTSSCPなどに使われていた、MML処理系一式+チップチューンエミュレーションのライブラリです。Google Codeにてソース一式公開しています。
T'SoundSystemでは、MasterChannel、Channel、Playerという概念があります。一番わかり易いのがPlayerで、各種ファイルフォーマットを解析して再生する役目を担います。今回はSMF対応のSmfPlayerを利用します。Channelは音源を表し、Playerにより操作されます。SmfPlayerからの操作を意図したMidiChannelがあったのですが、今回はMidiChannelから派生したWebMidiLinkMidiChannelを用意しました。最後のMasterChannelは波形合成を管理します。波形合成のストリーム管理と合成粒度を制御しつつ、Playerへのタイマーを提供しています。波形合成の粒度をPlayerが求める粒度と一致させることができるので、時間にブレのない演奏を実現します。また、複数のChannelを登録した場合には、ミキサーの役目も担います。WebMidiLinkでは、ソフトウェア音源のみを仮定しているわけではなく、また波形合成も各音源の責任によって行われます。よって、今回はPlayerへのタイマーのみを提供する、TimerMasterChannelを用意しました。

利用の仕方

以下にサンプルコードを掲載しますが、とっても簡単。WebMidiLink対応音源をiframeで埋め込む場合の例を示します。

<iframe id="wml" src="[WebMidiLink対応音源のURL]"></iframe>
// まずはWebMidiLinkMidiChannelを作成します。
// 引数にはMIDI信号を送るためのメッセージポートの口を指定します。
// これにより、受け取った演奏信号をWebMidiLinkに変換して出力します。
var midi = new WebMidiLinkMidiChannel(document.getElementById("wml").contentWindow);
// SmfPlayerを作成し、デフォルト音源として先ほど作成したmidiを登録します。
var smf = new SmfPlayer();
smf.setDevice(SmfPlayer.TRACK_DEFAULT, midi);
// TimerMasterChannelを作成します。
// SmfPlayerのsetMasterChannelで登録してあげると、登録されたMidiChannel
// 全てを指定されたMasterChannelに登録します。
// またPlayerのタイマーとしても自動的にMasterChannelを利用するようにします。

smf.setMasterChannel(new TimerMasterChannel(TimerMasterChannel.MODE_DEFAULT));
// 最後に再生。引数はMIDIデータを含むArrayBufferを与えて下さい。
smf.play(midi_data);

ArrayBufferについて

比較的最近に導入された仕様なので、知らない人向けに簡単に説明しておきますと、要するにJavaScriptで効率的にバイナリを扱うための仕組みだと思って下さい。例えば、Web経由で取得したSMFファイルをArrayBufferとして読み込むためには、以下の様なコードを書きます。簡単ですね!
var xhr = new XMLHttpRequest();xhr.open("get", "foo.mid", true);
xhr.responseType = "arraybuffer";
xgr.onload = function () {
    smf.play(xhr.response);
};
xhr.send(); 

バックグラウンドでの演奏について

このページ上でのデモはTimerMasterChannelの動作モードがMODE_INTERVALになっています。つまり、古典的なsetIntervalを使ってタイマーを実現しているのですが、1つ問題があります。Chromeで実行しているとタブを切り替えた際に演奏が極端に遅くなります。これを回避する方法はいくつかあり、TimerMasterChannelにも実装されています。引数として、MODE_TIMER_WORKERを指定すれば、バックグラウンドじの動作も安定します。MODE_DEFAULTでも現在はMODE_TIMER_WORKERが選ばれるようになっています。

まとめ

以上、駆け足でしたが、Web Audio APIと、それを使ったMIDIベースのソフトウェア音源の取り組みWebMidiLinkを紹介させて頂きました。また、どさくさに紛れて拙作ライブラリのT'SoundSystemも紹介しました。
これをきっかけにブラウザ上での音系アプリ開発に興味を持ってくれる人が1人でもいますように・・・。
なお、この記事はWeb Music Developers JP Advent Calendar 2012の一環として執筆されました。

2012年6月3日日曜日

ネットワーク関連の本、下から上まで


最近、面白そうなネットワーク本を見つけたので、それを含めて蔵書の紹介。どれも名著と呼ばれるものばかりのはずです。ただ、書いてみて気づいたんだけど、ここで一番上のレイヤーって言ってる本ですら、今時のほとんどの人にとっては目に触れる事のない最下層レイヤーかも・・・。それでも気になるって人だけ読んでください。

Principles and Practices of Interconnection Networks

京のネットワークコントローラを設計する際に散々お世話になった教科書。ルーティング、フロー制御、トポロジについて論理からハードウェア設計まで丁寧に説明されています。シミュレーションによる性能評価のノウハウも書かれてるので、この分野(インターコネクトアーキテクチャ)で研究しようと思ったら、これ一冊読むだけでスタート地点まで行けます。

Interconnections

古本が安く出てたので気になって購入。まだ読んでないんだけど、ハードウェアとIPの間を埋めてくれそう。ようするに、ひたすらL2中心の分厚い書籍(笑)。この本でようやく上から下まで繋がる気がします。

詳細TCP/IP (TCP/IP Illustrated, Volume 1: The Protocols)

日本語だと初版しか出てないのかな。SOFTBANK BOOKS版とピアソンの新装版があって、僕は古いSOFTBANK BOOKS版の頃に買って読みました。TCP/IPの基礎がすべて詰まっていて、ソフトの人が必要になるであろう知識としては一番下のレイヤーから書かれてます。たぶん、自作ウェブブラウザーを開発しようと思って買ったんじゃなかったかなぁ? 1997年です・・・。
有名なプロトコルについては簡単に説明が書いてあるのでRFC読むのが面倒な時とか、時々振り返って読んでます。会社の人に2版を進められてるので、そのうち買うかも。現在僕にとって一番勉強しておかないといけないレイヤー。

マスタリングTCP/IP SSL/TLS編

仕事でWebSocketに携わることになって。SPDYとの関係もあったのでSSLの理解は避けて通れない予感がして覚悟を決めて読んだ一冊。すべてを理解したとは言い難いかもしれないけど、だいぶ見通しは良くなったかな。なんだかんだでSSL周りのコードをいじる事もあるんですよね。上記TCP/IP本のセキュリティに関する補遺的な位置づけで読むべき本です。

UNIXネットワークプログラミング第2版 Vol.1 ネットワークAPI: ソケットとXTI

10年以上前に働いてた会社で初版を読みました。当時は会社で唯一のWindowsプログラマだった事もあり、WinSock2の本も読んでましたけど・・・あまり覚えてないなぁ。で、会社を辞めて大学に戻ってから、手元にも一冊ないと不便だなぁ・・・と買ったのが第2版。なので、結構読んでないところもあります。
ソフトウェア屋さんにとっては上の2冊が理論で、この本が実践。やっぱりソフトの方がハードより勉強する事多いのかなぁ。

2012年5月29日火曜日

いまどきのUNIXプログラミング

久々にblog更新しようとしたら、UIが一新されててビックリ。

さて、しばらく前の話になりますが、やや若い世代の人と集中的に開発を行う機会がありまして。「epoll使っていいですか、selectってあまり使った事ないので」と言われて愕然。当たり前と言えば当たり前なんだけど、90年代に身に着けたUNIXの知識もいまや年代物。少しはアップデートしないとなぁ・・・という事で本を読んで勉強したので、そのメモ。もしろん昔からあるけど知らなかったって事も沢山ありました。

読んだのは「LINUXシステムプログラミング」というO'REILLY本。400ページ弱という(この手の本にしては)薄い本なのだけど、興味深い話題が多く楽しんで読めました。以下、この本によってアップデートされた私の知識の項目一覧と概説。これを見て「おぉ」と思った人は仲間なので買って損はないと思う。

ファイル・I/O


ノンブロッキングI/O

O_NONBLOCKによるノンブロッキングI/O。知らなかったわけじゃないけど、自分のアプリでは使った事なかった。

同期I/O、ダイレクトI/O

fsync()とfdatasync()。あと、O_SYNC、O_DSYNC、O_RSYNCとか。O_DIRECTも。


ファイルサイズを超えたシーク

あぁ、これやるだけでsparse file作れたんだ・・・知らなかった。


ファイルポジション指定I/O

pread()、pwrite()とか。これはqemuの移植やってた時に見かけたのでちょっと前から知ってた。PepperのFileIOってpread()/pwrite()系列のAPIになってますね。


I/Oの多重化

select()ループがクールだった時代はとっくに終わってた。シグナルハンドラでできるのはpipeに書いてselectを起こす事くらい・・・と信じてた人はpselect()を調べるべし。あと知らなかったけどSystem V系ではpoll()とかppoll()というのがある。でも、今時Linuxでサーバ書くなら大量のデスクリプタに対してもスケールするepoll_*系関数群を使うべき。期待した通りのインタフェースを持ってる。Win32のWaitForMultipleObjectsより賢い多重化が可能になったと思う。


scatter-gather I/O

readv()とwritev()。リンクアレイチェーン転送的なI/Oインタフェースと言えば一部の人には分かりやすいかも。


ページサイズ取得

普段みかけるのはgetpagesize()ばかりなんだけど、こっちはLinux固有で、POSIX的にはsysconf(_SC_PAGESIZE)が正しいらしい。確かにNative Clientもsysconf()は持ってる。

I/Oスケジューラ

Deadline I/Oスケジューラ、Anticipatory I/Oスケジューラ、CFQ I/Oスケジューラ、Noop I/Oスケジューラ。4.6節でいきなりOSの教科書かと思うような細かいスケジューラの解説があります。なんでかと思ったら、Linuxは手軽にI/Oスケジューラが差し替えられるんですね。この辺は純粋なOS好きにとっても楽しめる内容かと。


拡張属性

xattr。時代はレジストリ、もといBFS。


ファイルイベント

inotify_*系のインタフェース。さらば、SIGHUP。


プロセス管理


プロセス階層

あんまり真面目に見て来なかったプロセスグループの話。


ゾンビプロセス

そういえば、なんでプロセス終了時に子プロセスを回収する処理をOSレベルで走らせないんだろ。ゾンビとして生かしておく根拠を知らないなぁ・・・。wait系は直接の子供しか待てないという理解なのだけど。


ユーザとグループ

プロセスのユーザIDは、実ユーザID、実効ユーザID、savedユーザIDの3種類ある・・・知らなかったし、一度読んだだけでは覚えられない・・・。


セッションとプロセスグループ

シェル書いてジョブコントロールとかしようとすると必要になる知識なんだろうけど。そう言えば、ちょっと前にLinuxに入ったスケジューラの改善って、この辺をうまく考慮してスケジューリングするとかなかったっけ?
※記憶と照合するとStaircase Schedulerの事だったのかなぁ・・・。でも、それももうobsoleteっぽい。ちなみに「Linux カーネル 2.6 Completely Fair Scheduler の内側」はスケジューラに関する良い記事な気がする。


デーモン

日頃お世話になっているdaemonさん。実は然るべき正規の手順が存在していたのですね・・・。新規プロセスグループのリーダープロセスにした上でchdir('/')して標準入出力は全て/dev/nullにする。わりと面倒な処理が必要みたいだけど、実はdaemon()ってライブラリ関数がある・・・というのも知らなかった。


I/Oの優先度

プロセス優先度だけでなく、ioprio_get()/ioprio_set()というインタフェースでプロセスのI/O優先度を指定できるらしい。ただしCFQ I/Oスケジューラ選択時に限る、か。


プロセッサアフィニティ

sched_getaffinity()とsched_setaffinity()。なるほど、SunOS+Oracle以外でも今や当たり前のインタフェース。


名前空間

Linuxでは(ファイルシステムの)名前空間はシステム固有ではなくプロセス固有。古典的なUNIXではシステム固有。Plan 9由来ですね。逆に、これがないとchrootってどうしてるんだろ。単にアクセス範囲をサブツリーに限定してるだけ?


メモリ管理


アラインメント

posix_memalign()。


/dev/zeroのmmap

そう言えば、そんなテクニックもあった。


高度なメモリ割り当て

mallopt()、malloc_usable_size()、malloc_trim()、mallinfo()あたりは知っていると使う機会もありそう。


スタック上への文字列コピー

strdupa() = alloca() + strdup()。C言語だけで開発する機会ってのも無くなってきてるので、知ってて役に立つかは微妙だけど。


ピンダウン

mlock()、mlockall()、munlock()、munlockall()。mincore()でスワップ判定。


オーバコミットとOOM

OOM Killerとの付き合い方。そういえば青山Killer物語の舞台って青山キラー通り?


シグナル

「古い実装ではシグナルを喪失することがありました。」いえ、それは私が知っているシグナルの実装、そのものです・・・。古い実装ではシグナル送信はプロセスにただ1つ存在するバッファにシグナル情報を保存して戻るだけの単純な実装で、プロセスは起きる前にバッファを確認し、存在していれば最新のただ1つのシグナルを処理してました。ところが、今時のシグナルを考えると、他にも考えなければならない問題が。
例えば、この本にも載ってなかったけど、マルチスレッド環境でシグナルを処理するのは誰(どのスレッド)か。答えは任意のスレッド。具体的な実装を見たわけではないけど、シグナルが積まれた後、最初に起こすスレッドが処理するのが一番ナイーブな実装か。特定のスレッドで受けたければ、他のスレッドはシグナルマスクを設定するのが作法らしい。


時間


時刻表現

micro秒のtimevalとnano秒のtimespec。最近はnano系が標準化されてるので、timespec系に一本化しとくと間違いなさそう。


POSIXクロック

clock_gettime()。Native Client内でも動いてるので重宝してます。


スリープ

秒単位のsleep()、micro秒単位のselect()ってのが古典の世界。今はmicro秒単位のusleep()もあるけど、nano秒単位のnanosleep()、clock_nanosleep()が標準化の観点からもオススメらしい。


タイマー

POSIXクロックの一部。time_*系関数群。


と、まぁ、こんな感じです。この本には出て来なかったけど、ttyとかも深い闇なんだよなぁ。未だに理解しきれてないプロセスグループやネットワークスタックなんかの話も含めて、いずれBSD本あたりを熟読したい次第。
 

2012年1月28日土曜日

安価な自作USBを作ってみる

VUSBを使った自作USBデバイスを作ってみたら、思った以上に簡単だったのでご紹介。

今まで自作USBデバイスと言うと、PIC18F2550を使ってました。これ自体は350円と安いのですが、標準フレームワークが純正C18専用ってとこが微妙。自分はSDCC向けにフルスクラッチでUSBスタック書いてたけど、USB関連のレジスタは簡単なビットフィールドの説明以外、詳しい触り方の情報がないので・・・まぁ普通はしないですね。

で、電子工作屋さんとしてもう一つ選択しに出てくるのがAVRシリーズ+VUSBという選択。こっちはハード的にはTINY2313でも動かせるので石の値段はたった100円で済む。ただし、ソフトウェア的にUSBの物理層を話そうとするので、USB 1.1 low-speedまでが限界。と言っても、自作レベルでそれ以上の転送レートが必要になるようなデバイスを作る事はそうそうないので、まずは用が足ります。

という事で、今回はVUSBの調査も兼ねてAVRで作ってみる事にしました。周辺に必要な素子は、自分の場合、回路箱から適当に見繕ってるんだけど。個別で集めるとなんだかんだで300円くらいになっちゃうのかなぁ。基板とUSBの端子、セラロック、4.7uの電解コンデンサ。あと抵抗とパスコンを少々。最後に3.3Vを作るためのお好みの回路。自分の場合、HIDaspx自作する時によくやってたのが、USBバスの5Vから電源用LEBを差して電圧降下で3.3Vを狙う方法。多少ずれても平気だけどホスト側との相性が出やすい、電圧が不安定になりがちでコンデンサで調整しないと動かない事も多々あり、たまにLEDが過負荷で飛ぶ・・・みたいな感じであまりオススメできません。今回はツェナーダイオードを使う方法で、2個直列させて安定3.3Vを作りました。ツェナー使ったことない人は向きを間違えないように注意。この辺りはチップが3.3Vを作ってくれるPIC18F2550のほうが圧倒的に楽で安心。

回路を作る上で注意しないといけないのは、リファレンス回路のままだと割り込みピンが全部持ってかれちゃうのとクロック出力が使えない、という2点。もしどちらかが使いたければ、ピン配置を変える必要ありです。ファーム側の変更は後述しますが、とても簡単なので遠慮なく変えちゃってOK。ただし、最低でもD+は割り込みピンに繋ぐ必要がある点だけ注意。ファームはD+のエッジをトリガにして割り込みで起動、クロックを数えながら物理信号を読み解くのです。同じくVUSBを使ってるHIDaspxなんかは、クロック出すためにピン配置変えてますので、回路のテスト用にとりあえずHIDaspxのファームを・・・という時には注意が必要。はまります(と言うか、はまりました)。特にヒューズの値が違うという点を忘れがち。

VUSBを使いながら、どれくらい自由にファームが組めるのか、という点が気になる所かと思います。前述の通り、物理信号の読み取りに割り込みハンドラが1つ取られます。あとは50msecに1回以上の頻度でusbPoll()を呼んであげればOK。usbPoll()の中では、割り込み側でパケットを検出していればホストからの要求を見て自動的に応答、あるいはユーザハンドラを起動して、ユーザからの応答を送り返す、といった処理をしてくれます。物理層読み取りと50msecの話があるので、リアルタイム制約の厳しい処理は共存できません。例えばソフトウェアUARTとかソフトウェアNTSC/VGAは書けないし、タイマー割り込みでPWM使って波形合成、みたいなのも安定させるのは難しそう。そういう時はもう1個マイコンを用意して、そっちでリアルタイム処理。チップ間はタイミング制約の緩いコマンドのやり取りで片付ける必要があります。

実際のファームの作成ですが、example以下にサンプルがあるので倣って作ればOK。USBのプロトコル知らなくても書けるレベルじゃないかな、これ。非常に簡単。ドライバ作らなくても良いって理由でHIDクラスのデバイスとして作る人は多いみたいなんだけど、自分はお気楽ドライバで好き勝手できるという点から、むしろカスタムクラスで作っちゃってます。
usbconfig.hというのがデバイスデスクリプタやピン配置を定義してるファイルで、マクロの値を説明に従って書き換えていけばOK。自分の時の書き換え例をここでdiffで見えるようにしてあります。D-のピン番号変えて、あとは要求電流を規約上最大の500mAにしてます。自作デバイスの消費電力とか、まず正確に把握するのは無理なので、とりあえず最大値を言っときましょう的な。実はMac使ってると、ここで宣言した以上の電力を使った時に「このデバイスの電力消費は多すぎ」みたいな警告ダイアログが出て、デバイスを無効化してくれます。ので、怒られたら増やして、みたいな対応もアリです。ベンダー/デバイスIDは個人で使ってる分には適当に。正規ID取得は結構お金かかるんだけど0x6666がPrototype product Vendor IDにアサインされてるんで、自分はこの値を使ってます。main処理はこんな感じ。usbInit()呼んだあとにusbDisconnect()状態で100msec以上待機、usbConnect()を呼んでからusbPoll()のループに入ってます。

プロトコル処理も今回は一番安易な方法。新規のエンドポイントを作らずに、デフォルトパイプでコントロール転送だけ使ってデバイス制御させてます。さっきのmain.cの中のusbFunctionSetupとlinux用ドライバのcrts_set() /crts_get()あたりを見比べてもらえば簡単に理解できると思います。

あとはMakefileの中で使ってるクロックを指定してやる必要があります。自分は20MHz使ってます。クロックによって物理層を読むときの遅延コードが変わるので、コードサイズにも若干影響します。他のクロックのほうが小さくなるって話を見たような気もしたんだけど、今手元で試した限りでは20MHzが一番小さくなってます。その他のファイルも合わせGoogle Codeに上げてあるので試しに何か作ってみようという方は参考にどうぞ。

2011年9月23日金曜日

Memo to build official android emulator

This is a memo to build official android emulator which Android SDK includes. This emulator forked from qemu.

Get source codes
$ cd $WORK
$ git clone git://codeaurora.org/platform/external/qemu.git
Build
$ cd $WORK/qemu
$ git checkout origin/aosp/tools_r13
$ ./android-configure.sh
$ make
Prepare OS images
$ cd $WORK
$ wget http://dl.google.com/android/android-sdk_r13-linux_x86.tgz
$ tar zxf android-sdk_r13-linux_x86.tgz
$ cd android-sdk-linux_x86/
$ ./tools/android
(Install SDK platform packages, then create virtual machine definition named X in GUI. Android 1.6, 2.3.3, and 3.2 will work fine.)
Launch the OS image
$ ANDROID_SDK_ROOT=$WORK/android-sdk-linux_x86 $WORK/qemu/objs/emulator-arm @X
(X is the name you named in preparing OS images.)
Memos

  • codeaurora.org is an unofficial mirror repository. If kernel.org come back, you must use the git://android.kernel.org repository.
  • If you'd like to build on OS X, you must build in a case-sensitive file system because block.h conflicts with system provided Block.h. Default file system is case-*in*sensitive, so you may prepare a disk image and work in it as follows;
    • $ hdiutil create $NAME -size 1024m -fs HFSX -volume $VOLUME; hdiutil attach $NAME; cd /Volumes/$VOLUME; do something...
  • If you'd like to build trunk sources, you may want to use prebuilt SDL library in /platform/prebuilt.git. It could be passed via --sdl-config arguments in android-configure.sh. But I guess it miss three functions including SDL_WM_GetPos, etc. Also it will be a pretty tough and boring work to find some stable combinations between versions of SDL, qemu, and OS images.

2011年9月19日月曜日

CPU Emulation using JIT on JavaScript

I'm struck with an idea to implement CPU emulation using JIT like technique on JavaScript. Basically, the emulator generates JavaScript function which emulate a target binary sequence by the basic block, then cache it in hash. The code could be like 'cache[pc] = eval(jit()); cache[pc]();'.

Since I'd like to know how this technique could be effective, I just make a tiny pseudo emulator and estimate its performance. The result show the JavaScript CPU interpretor will be x100 slower than a native code. Modern JavaScript engines become faster and faster. But it seems to be still slow. On the other hand, qemu-arm seems to be quite fast. It's just several times slower than the native one. So how about my emulation using JIT on JavaScript? It's slower than qemu. But not so bad. It might be pretty fast than native CPU interpreter.

As supplements, I must say that this benchmark treats just one simple case and the result will be changed by loop size, count, and JIT overheads in various situations.