IchigoJam(11) <LED&KEYを機械語で>
LED&KEY(TM1638)をIchigojamで点けてみた。BASICで入出力ポートを制御するとさすがに遅い。あまりに遅いので、マシン語で高速化してみた。
↑ダウンロード TM1638_Yoshi1.mp4 (1075.3K)
YOSHIがスクロールしているのだけれど、残像が残って認識できないくらい速くなっている。
福野さんがこのボードをマシン語で制御しておられる。(2018/4/14)
(格安7セグディスプレイ「LED&KEY」を使ってカチカチカウンターづくり、TM1638 x IchigoJam用マシン語SPIドライバの作り方)
どの部分をマシン語で書くか
このボードに載っているLED&KEY制御用デバイス(TM1638)はSPIもどきでデータを送り受けする。8個のLEDを制御しようとすると、 8(LED)x16bit/LED+3(command)×8bit/cmd=152bit のデータを送る必要があるから、クロックも152個必要だ。
このクロックをBASICのOUT命令でON/OFFしているから、データI/Oが152回、クロックが304回で合計456回IN/OUT命令を実行しなければならない。
高速化するならば、データを送り受けする部分をマシン語にすれば効果があるだろう。
メモリダンプ・プログラムを書いたときの手法でやってみる。IchigoJam(4) (2015/06/01)
Cで書いてgccでコンパイルして、逆汗して、BASICのPOKE文を作る。
逆アセンブラはgccのツールチェーンに入っているobjdumpを使った。
逆汗リストからPOKE文の作成にはawkを使った。今どきならpython、オヤジ世代ならperlだが、おじいちゃん世代はawkだ。
(IJUtils (http://ijutilities.micutil.com/#Software)のIJBin2Pokeを使うと簡単にできるらしい)
Cソース
Cのソースはこんな感じ。
/* |
TM1638にデータを送る関数はuint32_t send(uint32_t data)でデータを受ける関数はuint32_t recv(uint32_t dummy)だ。
IchigojamのUSR関数の解説では、hint16_t foo(uint16_t arg , uint8_t* mem, uint8_t* font)となっているが気にしない。
第1引数にはUSR(address , num)で呼んだときのnumがR0レジスタ経由で渡される、第2引数には仮想メモリの先頭アドレスが、第3引数にはキャラクタ・パターン・テーブルの先頭アドレスが渡されるが今回は使わないので宣言しない。
ARMの関数呼び出し規約では、4個までの引数はR0~R3レジスタを使い、5個目以上の引数はスタック経由で渡すらしい。 第2引数(uint8_t* mem)、第3引数(uint8_t* font)を宣言すると、R1とR2レジスタをコンパイラが使わなくなるので第2、第3引数は宣言しなかった。
マスクアクセス(masked access)
Ichihojamに使われているCPU LPC1114FN28には、マスク・アクセスという機能がある。福野さんのサイトに解説がある。
(マシン語でLEDを光らせよう! - IchigoJamではじめるArmマシン語その4)
一般に出力ポートの特定のビットを制御するときには、他のビットに影響しないようにする必要がある。
C言語では、出力ポートPxに'1'や'0'を出力するには、Px |= (1<<BIT)、Px &= ~(1<<BIT)
と書くことが多い。
もっと詳しく書くと W <- Px
W <- W & (1<<BIT)
W <- DATA << BIT
Px <- W
という処理が必要だ。つまり、変数Wに一旦ポートのデータを読んできて書き換えたいビットだけ書き換えてもう一度ポートに出力する処理が必要になる。
簡単に言うと、この面倒な処理を一度のアクセスで実現するのがマスク・アクセス機能で、GPIOのベース・アドレス付近にこの機能が割り当てられている。
C言語的には、ベースアドレスに配列 uint32_t MSKED_ACCESS[4096] があって。配列の要素がそれぞれビット・パターン(マスクパターン)に対応している。
例えば、IchigojamのGPIO1のbit(5)に接続されているLEDを制御する場合。
GPIO1のベースアドレスは 0x50010000で、データ・レジスタ GPIO1_DATA は 0x500103FFCにマッピングされている。
LEDを点灯させるには GPIO1_DATA(5)に'1'を出力すればよいから
GPIO1_DATA |= (1<<5);
となる。これをマスク・アクセスを使うと
GPIO1_MASKED_ACCESS[1<<5] = (1<<5);
でよい。出力する値は I<<5= 0b00010000でなくても、5ビットが1であれば良く、0b11111111でも良い。
Cで書くと手間はあまり変わらないが、マシン語に翻訳すると
GPIO1_DATA |= (1<<5);
movs r3, #32 ; r3 <- 1<<BIT_LED |
GPIO1_MASKED_ACCESS[1<<5] = (1<<5);
movs r2, #32 ; r2 <- 1<<BIT_LED |
コードは短くなっている。
コードが減るより重要なことは、ポートアクセスは一旦ポートの状態を読んでいるので、処理中にポートの状態が変わってしまうと誤動作してしまう。 ところが、マスクアクセスはポートの状態を読み出さないので、誤動作する心配がないということである。
ウエイト(時間待ち)
Cのソースはごく普通に16/8bitのデータを受けて1ビットづつ出力する処理だ。
TM1638のデータシートを見るとクロックの最小パルス幅(PWCLK)は400ns、データセットアップタイム(tSETUP) データホールドタイム(tHOLD)はそれぞれ100nsとなっている。
BASICは遅いからクロック出力はOUT 2,0/OUT 2,1で良いが、
マシン語に置き換えると速すぎる。'1'又は'0'を出力して400nsは出力データが変わらないように時間待ちする必要がある。
最適化
gccは-Oオプションで最適化レベルを指定できる。実行速度よりコード領域を最小化するオプション -Os は強力だ。 Ichigojamのようにマシン語で使える領域が少ない場合には有難い。
ところが、-Osオプションで最適化すると、ループを回って時間待ちするルーチンは最適化されて無くなってしまう。
void wait(void { int i; for (i=0;i<100;i++) ; }
の結果はどこにも影響を与えないので無くても良いということだけど、ちょっと困る。
最適化されないようにするには、wati()関数内で volataile属性の変数にアクセスすれば良い。 volatile属性は、例えばIO関係のレジスタや割り込みで使用される変数のようにアクセスするたびに内容が変わるという意味だ。
つまり、コンパイラに「あんたが知らないうちに値が変わってる変数だヨ」と教えておくと、最適化しないでくれる。
これをコンパイルして、wati(),recv(),send()をBASICのPOKE文にすると
REM wait() |
BASICソース
BASICのプログラムはこんな感じ
1 REM TM1638 demo2 |
コマンドは8ビット、データは16ビットで送受信するように変えた。
メモリ容量が足りないので、ソースの中にマシン語が書けない。
マシン語をメモリに書き込む処理は最初に実行すれば良い。そしてプログラム実行中に書き換える必要性はないので、プログラム実行前にダイレクトモードで実行すればよい。メモリにセーブしたい場合には、別プログラムにしてLRUNで呼び出すことになる。
1024バイトに収まるようにパックすると、
10 'TM1638 demo2 |
マシン語
わざわざ窮屈なメモリ環境でBASICからマシン語を使わなくて良いと思う。 Cで書いていると「全部Cで書いたらいいじゃないか」と思ったりして...
趣味やパズルだと考えるとやる気がわいてくるのだけれど。^^)
- IchigoJam(2015/03/30)
- IchigoJam BASICのFOR/NEXT(2015/04/05)
- IchigoJam BASICのFOR/NEXT(2)(2015/04/07)
- IchigoJam(2) -IchigoJamそっくりさんを作る-(2015/05/17)
- IchigoJam(3) -超小型ARMマイコン基板- (2015/05/)
- IchigoJam(4) -モリダンプ・プログラム-(2015/06/01)
- IchigoJam(5) -HDSP0670を使った4digit clock-(2015/09/24)
- IchigoJam(6) <4桁時計ダイナミック表示>(2015/10/13)
- IchigoJam(7) <OUT8~OUT11使用 4桁時計ダイナミック表示>(2015/10/19)
- IchigoJam(8) <8x8LED>
- IchigoJam(9) <8x8LEDで8Qeen問題>(2015/11/10)
- IchigoJam(10) <8x8LEDを光センサーにする>(2015/11/17)
- IchigoJam(10) <LED&KEY>(2018/04/08)
- IchigoJam(11) <LED&KEYを機械語で>(2018/04/15)
最近のコメント