2009年6月25日木曜日

AVRでも使ってみようかと

今までマイコンはPICを使い続けてたんですが、ATTiny2313が機能は少ないまでも100円というお手軽価格だったりするので、ちょっと使ってみる事にしました。

AVR Writerはどうしようかなぁ・・・と思って探してみるとhidaspx(*1)がUSBから使えて便利そう。っていうかAVRはソフトウェアUSBの実装がフリーで公開されているというのが驚き。上記の100円AVRをUSBコントローラとして使えてしまいます。AVR Writer自身にAVRを使うので、鶏と卵問題があったりしますが、確かAKI-PICでもAVRに書けたよなぁ・・・という気軽な気持ちで配布プリント基板をぽちっと購入。基板を待つ間にファームをAVRに書き込んでおくか・・・と思ったら・・・AKI-PICはATTiny2313に対応していなかった。。。ショック。

という事で、まずはAVRSP-COM(*2)互換のWriterを作ることにしました。最初はRS-232Cの信号から5Vを作るつもりだったんだけど、AVRSP-COMのプロトコル的に常時アサートされてる信号がなかったので、諦めてUSBから電源をとることにしました。最近は自作回路の電源をとる時はもっぱらUSBを使ってます。USBなら電力制限で保護がかかってるので、うっかりショートさせてもブレーカー落ちないし(笑。

で、次に作ったのが購入した基板で作ったhidaspx。わりと融通のきく回路になってて、基板と一緒に買えなかったものは手持ちの部品で事足りました。これだけでUSBデバイスとして動くのだから凄いですね。

その後、avraを使ったアセンブラでの開発をちょろっと体験し、すぐに面倒になってavr-gcc環境を構築しました(汗。まぁ、速度が必要になったらgccからインラインアセンブラ使えばいいや・・・って感じで。動作確認するまではWindowsで作業し、うまくいったのでメインマシンのMacで環境を整えることにしました。avr-gccはMacPortsからインストールして、hidspxは自前でコンパイル。なんとなくGUIも欲しかったので、Windows版hidspx-GUIを参考に見た目移植をしてみました。

開発環境はtcl/tkなので、MacでもLinuxでもOK。なんでtcl/tkなんか・・・ってのはご愛嬌。最近CADでtclを書く事が増えてきたので、その練習も兼ねて。M.Hiroさんのページ(*3)とか、「もっとTcl/Tk(*4)」を参考にさせてもらいました。前者のページはOh!Xとか、懐かしいですねぇ(しみじみ)。一応このGUIもアップしてみました(*5)。一部機能が未実装かつATtiny2313でしか動作確認していませんが。。。

という事で、まずはお約束の8x8 LEDを使って遊んでいます。LEDの制御だけでI/Oがわりといっぱいいっぱいなので、LED用の出力ポートを時分割でボタン入力にも使ってみました。子供のおもちゃにいいかもw


*1: 千秋ゼミ AVR/HIDaspx00
*2: ELM AVRライタの製作
*3: Tcl/Tk お気楽 GUI プログラミング
*4: もっとTcl/Tk
*5: hidspx-GUI/tk ver. 0.5.0/0.1

2009年3月2日月曜日

Java 3種の神器

最近eclipseを使う機会が増えました。Ruby on RailsとかOVMとかで良く使ってます。メインはOVMかな。そうなるとわりとJavaのお世話になる機会も増えたりして。例外はいて落ちたりすると中を追いかけたくなるのが人情というもの。という事で便利に使ってるのが以下のツール。
  1. Jad (decompiler)
  2. ClassFileAnalyzer (disassembler)
  3. jasmin (assembler)
1つ目はいわゆる逆コンパイラですね。classファイルにはメソッド名やメンバ変数名に関する情報も残ってたりするので、かなり可読性の高いコードに戻してくれます。とりあえず落ちた場所の周辺をざっと見るのに便利です。んで、単純な場合はこのまま逆コンパイルで得られたソースを直して、再びコンパイルすれば片付くんですが・・・解析が不完全に終わるとコンパイルできないソースを吐く事が多々あります。そんな時は2つ目のツール。こいつはいわゆる逆アセンブラで、classファイルを3つ目のツールに対応したアセンブラコードに変換してくれます。そんなわけで3つ目はアセンブラ。逆コンパイルが不完全な時にも、2と3のツールがあればソースに戻してからパッチを当てて、再びclassファイルに戻す事ができます。HAS、HLK、DISがあればなんでもできた、古き良きX68kユーザ時代を思い出します。

*1: http://web.archive.org/web/20080214075546/http://www.kpdus.com/jad.html
*2: http://classfileanalyzer.javaseiten.de/
*3: http://jasmin.sourceforge.net/

2009年2月15日日曜日

The Benchmark Handbook

ベンチマークについての古い資料で、昔はウェブで無料公開してたものらしいんだけど。今は公開サイトのドメインも失効してしまい、Morgan Kaufmannから出てた書籍も中古(しかも、わりと高価)でしか入手できない・・・と思ってたんだけど、試しにweb.archive.orgにかけてみたら、PDF含めてしっかりと残ってました。

というわけで、The Benchmark Handbook Online版。たまに論文に出てくるThe Wisconsin Benchmarkについて、それなりに詳しく書かれた唯一の資料でしょうか。

2009年1月31日土曜日

つまらないけど重要

趣味で開発している時には問題にならないけど、仕事で開発となるとついてまわるのが仕様書の問題。特に大きい会社では仕様書を書くのに割く時間は馬鹿にならない。ソフトウェアの場合、doxygenみたいなツールを使えば、わりとつまらない図を書いたりする時間も削減できたりするんだけど、ハードウェアだとなかなか良いツールがない(知らないだけ?)。

という事で、ソースから図を自動生成しようかな・・・とか考える。とりあえず
  • スクリプトでお手軽かつ適当に生成したい(テキストベースが楽ちん)
  • Wordに貼付けた際に汚くならない(ベクトルデータが良い)
って方針ですか。そう考えて思い出したのがSVG。XMLが出てきた当時、どう使うと便利なんだろう・・・とか思い悩んだもんですが、今や空気みたいなもんですね。出力するにはテキストが楽。でも入力時に解析するにはバイナリの方が楽。。。そんなわがままを受け入れてくれるのがXMLのうれしいところです。

さっそくSVGの仕様書(*1)を眺めてみたわけですが、結構シンプルで簡単。仕様書にも図付きでサンプルが載っていて、みながら数分で基本的な図形は作れるようになります。あとは、ここで理解したタグをスクリプトから自動生成してやるだけ。RTLからの変換にはRubyを使いました。って事でできたのが下の図。こんな馬鹿げた図でも、VisioやExcelでヘロヘロ作っていたらどんどん時間が飛んでいきます。くだらない事にタラタラ時間使って「忙しい」とか言う人間にはなりたくないものです。作業の効率化は研究者、技術者にとって永遠の課題です。

ソースはこんな感じ。お手軽ですよね。
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<g>
<text fill="black" x="62" y="50" font-size="6">RI2S1_DATA</text>
<line stroke="black" stroke-width="1" x1="50" y1="53" x2="250" y2="53"/>
<polygon fill="black" stroke="black" stroke-width="1" points="244,50 244,56 250,53"/>
</g>
... 中略 ...
</svg>
生成したSVGのプレビューや変換にはInkscape(*2)を使いました。Ubuntuからはapt-getでヘロっとインストールできました。EPSへの変換ができます。SVG Import Filter(*3)を使えばOpenOffice経由で色々と変換も可能。いきなりWMFとかにも出来るんだけど、グループ化を解除すると黒い豆腐になる問題があって、どういう経路で変換するのがベストかは今のところ模索中です。

*1: Scalable Vector Graphics (SVG) 1.1 Specification
*2: Inkscape
*3: SVG Import Filter for OpenOffice 2.0

2009年1月4日日曜日

LinuxでDLLを流用する

XMMSのpluginなんかでは昔から使われていた方法ですが、wineを使ってDLLを読み込む方法があります。・・・というか、ありました、なのかな?

古いwineの実装では、PROCESS_InitWineとか使ってexec的な事ができたんだけど、今は実装が変わっちゃってるみたい。

wine/library.hを見ると、wine_dll_loadとかwine_dlopenなんて関数があって期待しちゃうんですが、こいつらはwine_initを叩いた後でないと動作しません。ではwine_initを・・・とか思うと、ntdll.dll内で落ちます。実は(少なくとも今現在のcurrentに関しては)wine_initだけでは不十分で、wine_pthread_set_functionsという関数を使って、スレッド回りの処理をする関数群をまとめた関数テーブルをセットしてやる必要があります。この関数はlibrary.hには見えてこないけど、objdump -T libwine.soで確認でき、実際にextern指定してあげればリンクして呼び出すことができます。ではここでセットするwine_pthread_functions構造体を用意すれば良いのか・・・と思って準備を進めると・・・必要となる関数は、
  • init_process
  • init_thread
  • create_thread
  • init_current_teb
  • get_current_teb
  • exit_thread
  • abort_thread
  • pthread_sigmask
これだけあります。プロセスとスレッドの管理はまぁpthreadをラッパーを書いてあげれば済む話なんだけど、TEB関連はちょっとヘビー。細かいことは1年くらい前にmixiで書いたのでそちらを参照してもらうとして、ようはセグメントレジスタをいじって、特殊なアドレスにスレッド情報を格納しておく必要があるって事。ここまでいくと、wineコマンドをスクラッチから書くのとたいしてかわらない手間だという事がわかる。

そうすると、いっそのことexecでwineを叩けばいいじゃんって話になって、結局は必要なDLLを読むEXEを作ってあげて、forkした先の子供でwineをexec。あとは親子でプロセス間通信をして用を足す、って流れに。とりあえずこの方向でやる場合、pipeとmmapで通信できる事は確認してあります。

サンプルコードを書くとこんな感じ。どっちかの方法というよりかは組み合わせて通信するんだろうな。pipeで同期化も兼ねてコマンドを指示。データはmmapの共有メモリにて、って。pipeだとデータの管理が大変だし、共有メモリのポーリングは辛そう。

他にも自作のWinBeを使うって方法もあります。現在、DarwinとLinuxにも移植してあって、勝手しったる〜ってやつで。こっちだとWin32とLinux両方の実行環境を1つのプロセス内に混在させる事ができるんですが・・・如何せん、ひとりで作ったものなので、wineに比べると圧倒的に互換性が低いのが難点です。それでもcodecやら、リンクしたいなと思う類いの物なら大抵動いてくれるので、それなりに重宝します。


-------- winmain.cpp --------

#include <windows.h>
#include <stdio.h>

int WINAPI
WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HANDLE hFile = CreateFile("shared_file",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (INVALID_HANDLE_VALUE != hFile) MessageBoxA(NULL, "CreateFile: success", "TEST", MB_OK);
HANDLE hShare = CreateFileMapping(hFile,
NULL,
PAGE_READWRITE,
0,
1024,
"shared_memory");
if (INVALID_HANDLE_VALUE != hShare) MessageBoxA(NULL, "CreateFileMapping: success", "TEST", MB_OK);
char *sharedBuffer;
sharedBuffer = (char *)MapViewOfFile(hShare,
FILE_MAP_WRITE,
0,
0,
1024);
if (NULL != sharedBuffer) MessageBoxA(NULL, "MapViewOfFile: success", "TEST", MB_OK);

snprintf(sharedBuffer, 1024, "this message is send via mmap");
MessageBoxA(NULL, "Write to Memory Mapped File", "TEST", MB_OK);

DWORD dwWrote;
BOOL rc = WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
"hello from exe",
15,
&dwWrote,
NULL);
if (rc) MessageBoxA(NULL, "WriteFile to STDOUT: success", "TEST", MB_OK);
else MessageBoxA(NULL, "WriteFile to STDOUT: failed", "TEST", MB_OK);

char readBuffer[256];
DWORD dwRead = 0;
rc = ReadFile(GetStdHandle(STD_INPUT_HANDLE),
readBuffer,
255,
&dwRead,
NULL);
readBuffer[dwRead] = 0;
char message[512];
snprintf(message, 512, "ReadFile from STDIN: %s", readBuffer);
MessageBoxA(NULL, message, "TEST", MB_OK);

return 0;
}


-------- main.cpp --------

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/mman.h>

int
main
(int argc, char **argv)
{
int elf2exe_pipefd[2];
int exe2elf_pipefd[2];

if (-1 == pipe(elf2exe_pipefd)) perror("pipe: elf2exe");
if (-1 == pipe(exe2elf_pipefd)) perror("pipe: exe2elf");

pid_t cpid = fork();
if (-1 == cpid) perror("fork");

if (cpid == 0) {
// child (exe)
close(STDIN_FILENO);
close(STDOUT_FILENO);
if (-1 == dup2(elf2exe_pipefd[0], STDIN_FILENO)) perror("dup2: elf2exe");
if (-1 == dup2(exe2elf_pipefd[1], STDOUT_FILENO)) perror("dup2: exe2elf");
close(elf2exe_pipefd[0]);
close(elf2exe_pipefd[1]);
close(exe2elf_pipefd[0]);
close(exe2elf_pipefd[1]);

char *exec_argv[] = {
"wine",
"test.exe",
NULL,
};
execvp(exec_argv[0], exec_argv);
// no return
} else {
// parent (elf)
close(elf2exe_pipefd[0]);
close(exe2elf_pipefd[1]);
int fd_rd = exe2elf_pipefd[0];
int fd_wr = elf2exe_pipefd[1];

char buf[256];
ssize_t size = read(fd_rd, buf, 255);
if (-1 == size) perror("read");
buf[size] = 0;
printf("read from exe: %s\n", buf);
write(fd_wr, "hello from elf", 14);

int fd_map = open("shared_file", O_RDWR);
if (-1 == fd_map) perror("open");
char *shared = (char *)mmap(NULL, 1024,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd_map, 0);
if (NULL == shared) perror("mmap");
puts(shared);
fflush(stdout);
wait(NULL);
puts("parent process exit");
}
return 0;
}

2009年1月3日土曜日

Subversionでクライアントが固まる障害

年末から、Subversionでクライアントがupdate中にハングアップしたり、redMine側のレポジトリブラウザを開くとredMine自体が応答しなくなる、といった症状に悩まされていました。

色々調べてみたんですが、Subversionのデータベースが壊れた場合の方法しか出てこない。うちもこれなのかなぁ・・・と思ってレポジトリを直接覗いて調べていたら・・・

% ls -hl .../db/revs/
:
:
:
-rw-r--r-- 1 apache apache 1.4G 12月 23 04:49 5665
:
:
:

なぬ? 1.4Gの差分ってなんだよー・・・orz
ってことで、原因は大量ファイルの一括commitでした。さらに調べると、この時commitされたファイルのうち1つが650MBちかくある。・・・こいつが原因か!! むぅ・・・もう少し考えてcommitして欲しいなぁorz よくよく見るとcommitしてあるファイルを日にち付けてtar.gzで固めただけっぽいし。バージョン管理してるんだから、そういうの不要じゃん。

原因に気づき、update中のクライアントをtcpdumpで追いかけてやると、確かにハング中もHTTPで大量データのやりとりを続けてたorz 仕方ないので「でかすぎて有害なので消すよ」とコメントを付けてtrunkからremoveしました。これでredMineのレポジトリブラウザも生き返るかなぁ・・・。途中経過を内部で持ってたらアウトかもなぁ。最初のアクセスに凄く時間がかかった記憶があるので、内部で差分を変換してデータベース化してそうな予感なんだけど・・・。

2009年1月2日金曜日

お金をかけずにSystemVerilogを使ってみる

設計に関して言えば、まだまだ他のツールチェインとの相互運用の面で問題が起きやすく、手放しに喜ぶ事のできないSystemVerilogですが、検証に関して言えば逆にVerilogなんかじゃやってられません。会社で使う分にはライセンスに困らないので普通にIUSとかQuestaを使えば良いのだけど、個人で使うとなると・・・わりと選択肢がないです。

フリーなVerilog処理系で有名なのはIcurus Verilog。ウェブなんかではSystemVerilogにも対応してるとか書いてるページもあるんですが、それは嘘。引数の互換性を持たせるためにVerilogのバージョンを指定できるようになってますが、パーザは引数を見ずに、全てのモードで同じ動作をします。

という事で商用の評価版として重宝しているのがModelSim。ModelSim Xilinx Edition-III Starterが制限付きながらも利用できます。Windows版しか利用できないのが残念ですが・・・。あくまでインストールしたコンソール端末だけで利用せよ、との意図で、リモートデスクトップからログインしているとライセンス違反で起動できません。そうなると、Cygwinを入れてsshdを立てておき、sloginしてコマンドを叩く、って使い方をするしかありません。もちろんちょっとした試験にはこれで十分なんですが、波形を表示しようとするとちょっと面倒。wlfをvcdに変換してscpしたり、gtkwaveを使ったり。あるいはVNCを使うのもありですが。。。もう少しなんとかならんかなぁ、って事でLinux+wineにインストールしてみました。って事で、以下書きメモです。

  1. Configure Wineのアプリケーション設定でWindowsのバージョンを「Windows 98」に設定。
  2. Configure Wineのドライブ設定でCドライブの詳細設定を選びシリアルを適当に設定(8桁の[0-9A-F]です)。
  3. インストーラから標準インストール(mxe_3_6.3c/setup.exeを使いました)。
  4. ライセンス申請
    • インストーラからの流れでうまくいかない場合、Modeltech_xe_starter/lic_request.txtの6行目に書かれているURLを叩けばライセンス申請に入れる
    • lic_request.txtで最後から2番目の行に「Disk Serial Number」の行がなければシリアルの取得に失敗してるので、上記のURLのQueryにも「ds=xxxxxxxx」(xxxxxxxxは2で設定したシリアル番号)を追加してやる必要あり。
こんな感じでうまく動いてそうです。あとはvlib.exe、vlog.exe、vsim.exeあたりに

--- /usr/local/bin/vlib ---
#!/bin/sh
exec wine $HOME/.wine/drive_c/Modeltech_xe_starter/win32xoem/vlog.exe $*
------------
こんな感じのラッパーを用意してやれば、ほぼLinux版のつもりで使えそうです。とりあえずovm-2.0.1のexample/tlm/tlm_fifoが動くのは確認しました。

そんなこんなで、近々OVMあたりも紹介したいなぁ、と思ってます。

追記(2009/1/4):バージョン設定についてはmodelsim.exe、vsim.exe、vish.exeあたりを個別に行っておけば、Default Settingsは変えずにOKっぽいです。