2023年10月22日日曜日

自作CPUで遊ぶ 11

CNCはG-codeで制御することになるが、主軸の移動を指示するコードにG00(位置決め)とG01(直線補間)がある。G00は主軸の原点復帰等のような位置決めに使われる。この時各軸は同期せず個別の設定値で移動する。起動/停止時の加速度もそれぞれの設定値を使うので、例えばX/Y各軸の設定値が同じ場合主軸は最初に45度の方向に移動し、何れかの軸が目標位置に到達すると今度はX方向またはY方向のみ移動して目標点に到達することになる。これに対してG01の直線補間(切削送り)は現在位置から目標位置までの直線上を移動する。前々回に検討したのはこの場合になる。
で、これらの処理のFPGAのロジックとソフトウェアの分担について検討した。 パルスモータードライバーへのパルス生成方式は基本的にはHDD用スピンドルモーター駆動時の方式をベースにするのだが、とりあえず以下のような入出力で考えている。
*_pm_***はパルスモータードライバへの制御信号だ。em_stopは非常停止信号でこれが1の期間は動作を停止し*_pm_enも0にする。s_beatはビートパルス信号で、起動/停止時の加減速はこのパルスに同期して行う。*_pcntは*_pm_pulseに出力されるパルスの計数値だ。*_pm_dirの値によって増減する。*_pcntは*_clrで0にリセットされる。*_clrは原点設定時に1になる。X/Y/Z各軸のパルスモーターの回転が各軸の移動に変換されるので、*_pcnt値に1パルス当たりの移動量を乗じれば原点からの座標になる。*_accelは起動/停止時の加速度、*_speedは目標速度だ。主軸が停止状態から移動が始まるとすると、速度0から *_accelのレートで増速していき *_speedに達する部分で *_speedに固定する。また、停止時は *_speedの速度から *_accelのレートで減速して行きやがて0になる。 *_enは起動/停止を制御する。 0から1に変化すると移動を開始し1から0になると停止する。停止時は直ちに停止する訳ではなく減速しながら停止する。起動から完全停止迄の期間のパルス数が移動量になるので停止のタイミングはちゃんと計算する必要がある。 

で、FPGAのLOGIC側をこうするとすると、zumi32側の処理は少なくとも以下のことをする必要がある。
数値計算はfloatで行う必要があるが、これは問題ない。Dpを求めるのに√演算が必要になってしまった。これを何とかする必要がある。zumi32は演算用リソースとしては加減算器とバレルシフタしか持っていないのでfloatの四則演算はそれなりにステップ数を要する。√演算となると尚更だ。そこで、この部分はハード化することにした。√演算器は10年くらい前に趣味で自作した。最初のやつはパイプライン式で組んだが回路が大きくなるのでその後に非パイプライン化版も作った。これは1bit@1clockで計算する。32bit floatの仮数部の√を求めるのでトータルで26クロック掛かる。これだとちょっと掛かりすぎな気がするので、今回2bit@1clockで計算するように変更した。
この演算器をzumi32に組み込み、sqrt命令を追加することも出来るが、他のfloat系演算命令は未実装なのでこれだけ入れるのはバランスが悪すぎる。今回はfp_sqrtモジュールとしてi/o busに接続して使うことにした。また、こうするとfp_sqrtにデータを書き込んだ後、結果がでるまでzumi32で他の処理をさせることも可能で処理を並列化することも出来る。といってもfp_sqrtのレイテンシーは13なのでその期間だけの話だが。。。 以下は動作確認のために現状のプロジェクトに組み込んだ様子。
zumi32のプログラムは以下のようにした。
simulationの結果
vivadoでimplementして見た。100MHzで動作させるがタイミングエラーは無くMetした。回路規模も意外と小さい。

2023年10月15日日曜日

自作CPUで遊ぶ 10.5 ? (卓上旋盤)

YouTubeでCNCや旋盤でのものづくり動画を見るのが好きで、見ると止まらなくなっちゃうので普段は見ないようにしているのだが、今日うっかり見てしまったら無性に自分でも何かを組み立てたくなってしまい、気がついたらジャンクのアルミ材を無心にネジ締めしてた。 ふと、我に返って笑ってしまった。何やってんだ俺?・・・と。
しかし、組み立ているものを見たら先週実験した卓上旋盤のベース部分に使えそうな気がしたので、そのまま組み上げることにした。
卓上旋盤化するにはベースにするアルミフレームを購入する必要があり、金掛かるしどうすっかなーと悩んでいたのだが、まさか手持ちの部材で出来るとは。。。考えもしてなかった。やっぱ、人生持つべきものはジャンクだわ。と改めて思う。 

上の写真の状態ではまだ完成ではない。制御基板(FPGAボード)や電源も必要だし刃物台も必要だ。 所有しているCmod7は1枚だけでこれはCNCの方で使うつもりなので、この卓上旋盤用には別のFPGAボードが必要だ。回転数や正転・逆転の設定や表示、非常停止制御ぐらいは出来るようにしたい。 もちろんzumi32を実装する。

ま、これもぼちぼちやってくつもりだ。


2023年10月9日月曜日

自作CPUで遊ぶ 10

所有しているCNC3040には三つ爪チャックが付属している。モーターはパルスモータでチャックとモーターとのプーリー比は1:3だ。 これを回してみた。このチャックは購入してから一度も動かしていなくて今回初めて動作させてみた。

普通に回すだけなので問題無く動いた。ベースボードに固定すれば卓上旋盤的な物が作れそうだ。
何かワクワクする。 (^_^)

CNCでX-Y2軸を制御してスピンドルの位置を移動させる場合、移動先の座標値の与え方には幾つかの方法が考えられる。普通に考えればX,Yの直交座標系で各軸の移動量を与える方法だろうが、極座標系で角度と目標位置までの直線距離を与える方法もあるだろう。 何れの方法も座標値は絶対座標の場合と相対座標の場合が考えられる。 何れの方式にしても、実際に制御するのはX/Y各軸のパルスモーターになるので、最終的にはこれらのモーターに印加するパルス数とパルスレート(周波数)に変換することになる。パルスモーターには最大自起動周波数という特性があって、いきなり起動・停止できる最大パルスレートに制限がある。なので、パルスモーターを起動・停止するときは徐々に加減速する必要がある。つまり、加速度に上下限値が存在する。CNCを制御するソフトウェアにはMach3やLinuxCNCがあるが、これらのソフトウェアでもこれらを設定するパラメータがある。

 Mach3の設定画面

即ち、X/Y各軸のモーターに対してこの加減速制御をする必要がある訳だが、如何なる場合にも同じ値とは行かずこれらの値は点P0からP1への直線移動の加速度とし、これをX/Y軸の加速度に変換する必要がある。
スピンドルの移動速度もG-code等で設定されるので、X/Y軸の移動速度は以下のようになる筈だ。
つまり、G-code等で与えられた各値からXpulse、Xspeed、Ypulse、Yspeedを求めてパルス発生部に入力する必要がある。どこからどの部分までをFPGAロジックに実装し、他の部分をzumi32に実装するかを検討する必要がある。


2023年10月1日日曜日

自作CPUで遊ぶ 9

定年になって再雇用社員になったというのに何故か仕事がめちゃくちゃ忙しい。給料はほぼ半減したのに下手したら定年前よりも忙しい。先日は仕事帰りに急に喉がヒリヒリ痛く微熱も出てきた。職場で高熱で休んだ人がいるという話も聞いていたので、これはいよいよ私も何かに感染したかしら?とか思ったのだが一晩寝たら回復した。かなり疲労が溜まっているので免疫力が低下しているのかも知れない。 こんな時は好きことをやって気分転換だ。…やってることは仕事とあまり変わっていない気がするが… 全部、自分の思い通りに・好きなように・作りたいものを作る。というのが気分転換になる。 

さて、今度はステッピングモーター(パルスモーター)を制御して見たくなってジャンク箱からモーターを出して来た。 パルスモーターの制御を習得したいなーと思って集めていたら、いつのまにか沢山所有してしまった。 ここに写っているのはその内の一部だ。
モータードライバはDM556Dというやつを使うことにした。これもいつかこれで遊ぼうと思い数年前にAmazonで購入して死蔵していたやつだ。 回転させるだけならHDDのスピンドルモーターと同様の方法で良いが、位置決め制御等もやってみたいので今回はこれを使うことにした。
なので、FPGAからはEnable、Dir (回転方向)、Pulseの3種の信号をDM556Dに与える。
7年位前にCNC4030Zという中華製のCNCを購入した。今はまったく使っていないがまだ所有している。 この装置のコントローラはI/Fが古く、PCのプリンタポートと接続する必要がある。また制御するソフトウェアもWindows95用のものなので今動かすの色々と難しい。 最終的には、コントローラと制御ソフトウェアを自作してこれを動かしたいと考えている。これで最高4軸迄は制御してみることが出来る。
とりあえず、普通に回転させてみた。プログラムはHDD用スピンドルモーター用のやつをベースに若干手直しして使った。 とても容易に回せた。このモーターの場合、回転数は9000 [rpm]が上限のようだ。

2023年9月18日月曜日

自作CPUで遊ぶ 8

前回のデザインをベースにしてCPUをMicroBlazeに変えてzumi32版との違いを色々と見てみた。MicroBlazeの設定はzumi32と同等にするために乗算器等は入れずにBarrel Shifterのみとした。
このデザインでImplementしUtilizationを見てみた。MicroBlazeの部分はSlice LUTsが1053だった。これに対してzumi32は1066と同程度の規模だった。但し、規模は同程度だが内訳はかなり違う。レジスタ数は770(MicroBlaze)に対して512(zumi32)。LUT as Logicは851(MicroBlaze)に対して1022(zumi32)。LUT as Memoryは7(MicroBlaze)に対して0(zumi32)。zumi32はF/Fをあまり使っていない。これが動作速度を上げられない要因なのかも知れない。
次に前回と同等のプログラムをMicroBlazeに実装し、生成されるコードの規模や処理速度を見てみた。コンパイルされたelfのtext、data、bssのサイズはそれぞれ 1588、276、3112だった。 これに対して、zumi32はLinkerが出力したmapファイルから各セグメントのサイズを見ると、textが0x284 (644)、dataが0xc (12)、bssが0 とかなり違う。textは約1KByteもの開きがある。最適化オプションは何れも-O2にしている。
MicroBlazeのelfを逆アセンブルしてmain()の部分を見てみた。命令数は31なのでかなりコンパクトだ。これで何故text sizeが1.588Kバイトになるのか謎だが、もしかするとstartup部や例外処理などのコードが大きいのかも知れない。 1KByteは1命令32bitなので命令数にして256命令分。。小規模なFPGAにCPUを実装する場合等は結構厳しいサイズかも知れない。
一方、zumi32をコンパイルして得られたアセンブラは以下のようだった。命令数が38もあるが、while loopの部分のみを抜き出してMicroBlazeのコードど比較すると以下のようだった。
zumi32の命令数が多いのは命令セットの違いもあるが、gccをzumi32にportingした時の私の工夫が足りていないのが要因だろう。 zumi32は遅延分岐があるが遅延スロット部をnopで埋めている。最適化の部分をもっと工夫すればMicroBlazeと同等にはなる筈だ。ちなみに手動で最適化すると命令数は14まで削減できる。
次に、それぞれのデザインとプログラムでXsimでsimulationしてwhile loopを1回実行する時間を見てみた。zumi32のプログラムはgccでコンパイルしたコードをそのまま使った。 上がzumi32、下がMicroBlazeの波形だ。MicroBlazeが11.37usecに対してzumi32は18.31usecと1.6倍も遅い。上で見たとおり命令数の差はあるが19:22なので1.15倍程度の差でしかない。したがって遅い要因は命令数の差だけでは無い。
速度を遅くしている要因はload命令のlatencyと分岐時のfetch unitとexecution unit間のlatencyにあった。そこでそれらを修正し、且つzumi32のコードを手動最適化したコードに入れ替えて再度simulationをしてみた。 その結果、while loop1回の処理時間は10.1 usecまで短縮することが出来た。
MicroBlazeより約12%速くなった。但し、手動で最適化したコードを使った場合なので現状のgccを使い続ける限りはMicroBlazeと同等になるんじゃないかな?と思う。 

前向きに捉えるとzumi32-gccにはまだまだ伸びしろがある。!!!ってことだが。。。これを改善するのは大変なんだよな。 portingに数年を要したからなー。

まぁ・・・、でも 最小構成とは言えベンダー製のCPUの性能にかなり近づけているというのは嬉しいことではある。

2023年9月10日日曜日

自作CPUで遊ぶ 7

外では鈴虫が鳴き始めている。酷暑も少し和らいできているが残暑はまだまだ厳しい。去年から私の部屋のエアコンが故障していてまったく冷えない。今年も在宅の時は部屋の温度は平気で36℃に到達していた。今この瞬間も32℃もある。まぁでももう少し辛抱すれば涼しくなるんじゃないかな。

さて、今日はモーターからちょっと離れて別のことをしてみた。

zumi32にAXI Master I/Fを付けてVivadoのIPを接続できるようにして見た。私はIPは可能な限り自作する派でベンダー製のIPはできることなら使いたくないと思っている。また、趣味で遊ぶ分にはそれで十分というのもあるのだが、ふと思い付いて zumi32にAXI Master I/Fを付けて見たくなった。 AXIにはPeripheral I/Oを接続することを考えていて、主記憶となるMemoryの接続は考えていない。そのためアクセスはシングルアクセスのみになるのでAXI-Lite相当のAXI-I/Fにした。それにAXI-Lite-Masterなら簡単にサクッと作れる。

以下のようなブロックデザインにした。AXI-GPIO2つとAXI-Timerを接続した。プラットフォームはCMOD S7を使っているので、それに実装されているカラーLEDと4つの単色LEDをそれぞれ別個のGPIOで制御できるようにした。
このデザインでまずはLEDを点滅させるプログラムを書いて実行してみた。以下はプログラムとBSPだ。
実機にダウンロードして動かしてみた。
   

普通に動いた。
これが出来るとAXI-I/Fを持った色々なIPを簡単に接続してシステムを構築できるってことだよな。

2023年9月3日日曜日

自作CPUで遊ぶ 6

手持ちのモーターの内どれが一番高速に回せるか見てみたら下の写真の左上のやつが一番高速に回せた。
これを無負荷(円板とか何も付けない状態)で回してみたところ 45000 [rpm]まで回せた。電源電圧は通常は5Vにしているがこの時は12Vにした。5Vだと27000 [rpm]位が上限だった。zumi32のプログラムは基本的に前回と同じで、回転数の上限だけを変えた。

次に磁気ディスクを装着して回してみることにした。そしたらこれまでと全く様相が変わりαx^4の制御曲線では回せなくなった。負荷が重くなったためと思う。モーターを脱調しないで起動出来る回転数の上限は無負荷時は300[rpm]位だったのだが、これが60[rpm]、つまり 1 [rps]になった。また制御曲線は一次式のαxにする必要があった。αの値は0.01になった。この曲線での回転数の上限は10500 [rpm]だった。(電源電圧は5V) ディスクが重いので慣性力(イナーシャー)が大きく、駆動を停止してもディスクの回転がなかなか下がらない。 当然だがこのディスクは偏心していないので高速に回しても音はそれほど大きくならない。

負荷の有り無しで結構違う。勉強になった。 
Youtubeに壊れたHDDを改造してディスクグラインダーにする動画があって、ウソクセーと思っていたのだが、これくらいのイナーシャーの付き方ならもしかしたら出来るのかも知れない。


2023年8月27日日曜日

自作CPUで遊ぶ 5

これまでのプログラムは1個のタイマーを使いタイマー割り込みハンドラ内で割り込み周期を段階的に変えることでモーターの駆動パルスを作っていたので制御が粗くなってしまっていた。 そこで今回はやり方を変えて、タイマー割り込みの周期は一定とし代わりにモーター制御用のパルス生成器を作ることにした。
パルス生成器の構造はシンプルにアキュムレータ方式とした。アキュムレータを acc とすると、accは毎クロック acc = acc + αで更新される。また、acc が閾値 N を超える場合は0に戻す。この時パルスが1つ出力される。下図はaccのαが1, 2, 3の場合のaccの軌跡を表している。現在回路は100MHzで動かしているのでNは100_000_000に設定する。すると、αが1の場合1秒に1回パルスが発生し、αが2の場合1秒に2回、αが3の場合は1秒に3回発生する。つまり、αはパルスの周波数を設定することになる。今回のモーターの場合パルス12発で1回転するので、モーターの回転数を RPS (Rotate Per Second)で表すとするとαは 12×RPS になるがこの変換はIP内で行うことにした。つまり、ソフトウェアは秒当たりの回転数を何らかの方法で算出して設定すれば良い。
次に回転数の生成だが、前回採取した波形から起動時の立ち上がりの部分(左下の緑色の曲線)を式化してそれを使って回転数を決定してみることにした。
色々試してみた結果、y = ax^4、a = 1.97499e-11 を使うことにした。 多項式にすると計算に時間が掛かるのでなるべく単純な式にした。タイマーの割り込み周期は1msecにして、割り込みハンドラで y = ax^4 でモーターの回転数 RPS を計算してIPに設定する。 aの値が1.97499e-11とかなり小さいので固定小数演算にすると32bitを超えてしまう。そこで計算はfloatで行うことにした。zumi32-gccはfloatも使えるのでfloat演算自体は問題ないが、zumi32の演算用リソースとしてはバレルシフタのみなのでコンパイル後のマシン語のステップ数と演算に要する時間が気になるところだ。 そこで、コンパイルしたマシン語列をシミュレーションで流してみたところ、割り込み処理時間は約28usecであることが判った。タイマー割り込み周期は1msecにするのでまったく問題ない。(シミュレーションではタイマの割り込み周期は50usにしている。)
で、これを実機で動かしてみたのだが結果はNGだった。起動直後に脱調してしまう。aの値を小さくしたりx^4をx^2にしたりして立ち上がりを遅くすると動くのだが最大回転数は50 [rps](3000 [rpm])が上限だった。そこで、モーターのdrv_A, drv_B, drv_Cの駆動パターンを変えてみることにした。具体的には従前は 011、110、101のパターンを繰り返しており常に2つのコイルを励磁するようにしていたのだが、これを001、011、010、110、100、101と1コイルのみが励磁されるパターンを入れることにした。 これにより今までは12パルス@1回転だったのが24パルス@1回転になるので、IPの設定値を12倍している部分は24倍に変更した。 この他にaの値を色々調整したりした結果 120[rps](7200 [rpm])まで動くようになった。 ヤッタ!!
以下はロジアナで採った波形だ。
Sensorの周期が120Hzなので7200[rpm]で回転していることが判る。
以下は動かしている様子。7200[rpm]ともなると風切り音がすごい。

ということで、zumi32でモーターを制御して7200 [rpm]で回転させることが出来た。また、zumi32のプログラムでもfloat演算を問題なく使えることを確認することが出来た。\(^_^)/

ERROR: Failed to spawn fakeroot worker to run ...

なにかと忙しくてなかなか趣味の時間を確保できない。 ...orz  家の開発機のOSはLinux Mintなのだが、最近バージョンを22に更新したところ、myCNC用のpetalinuxをビルドできなくなってしまった。ビルドの途中で ERROR: Failed to spawn ...