2024.01.31
2024.01.30
2024.01.28
MITOUJTAG 3Dのプログラム解析
昔いたアルバイトさんが作ったプログラムを解析しています。
まず、この部分。
MJ3D_Initという関数は、3Dビューのウィンドウが開くたびに呼び出される関数なのですが、
renderer_ptr ptr = new Renderer(w);
ということをしています。
Rendererというクラスがあるのは良いのですが、なぜ、わざわざtypedefでポインタ型のクラスを作るのか?うーん。わからない。
Rederer * ptr = new Renderer(w);
C#とかJavascriptとかでは基本的にクラスオブジェクトの実体ではなくポインタで扱うから、そのような感覚でプログラミングがしたかったのかもしれません。
このmap<int, renderer_ptr>というのは、idに対してRendererのポインタを返す連想配列を作っています。MITOUJTAGではたくさんの3Dビューが開くので、そのウィンドウごとにidという数字が割り当てられるのですが、その数字に対応したRendererのポインタを得たいのでしょう。
このプログラムを解析して知りたいことは、たくさんの3Dビューが開くプログラムをOpenGLでどうやって作るか。また、glutやglfwとの関係はどうなっているのかということです。
では、Rendererクラスの中を見ていきましょう。
あああ・・・😢
ヘッダファイルの中にメソッドを書きまくっています。
最近のコーディングスタイルなんでしょうかねぇ。ヘッダは宣言だけにしてCPPに本体を書くというやり方は古いのかもしれません。
さて、Rendererの引数に与えられたwというのは、表示したいウィンドウのハンドルです。MITOUJTAGはBorland C++で作られていて、それぞれのMDI子ウィンドウはTFrameというクラスの仕組みを使って描画しています。このwにはTFrameのHandleを渡しています。
hDC = GetDC(w);
auto pfdID = ChoosePixelFormat(hDC, &pfd);
で、TFrameのハンドルからGetDCをしてピクセルフォーマットを得ています。これが何をしているかはこのページに解説がありました。ふむふむ。おまじないのようなものですね。
ここではautoが使われていますが、中身はintです。
次に
hGLRC = wglCreateContext(hDC);
if (hGLRC == NULL) { exit(1); }
wglMakeCurrent(hDC, hGLRC);
詳しいことはわかりませんが、TFrameのHandleから作ったhDCを元にhGLRCを作っています。その後でライトの設定とかテクスチャの読み込みとか(テクスチャのファイルが開発者のPCのフォルダ構成に依存していた😞)を行っていました。
つまり、3Dのビューが開くたびにOpenGLのコンテキストを作っていて共有するとかはしていないようです。
で、RendererクラスにはRenderというメソッドがあって、呼び出されると、
glClearした後で
devices[i]->DrawDevice();
をしています。このDrawDeviceの中身は
です。 このobjという中にデバイスを描画するための座標やら色のデータが入っていて、それをここで描き出しています。
全貌が見えてきました。
JTAGデバイスが追加されると、objというオブジェクトの中にポリゴンの座標データが大量にぶちこまれ、それがRender関数の呼び出し時に描画されるという仕組みのようです。
3Dのグラフィックで言ういわゆる「シーン」というのはobjというクラスの中に全部入っていて、Render関数が呼び出されたときにfor文で全部をなめるようにして描いているようです。
2024.01.27
MITOUJTAGの3D化をするための古いコードを復活させたい
MITOUJTAGの画面が古臭いイメージなので、3Dの近代的なものにしようと思っています。
全部一から作り直すのも大変なので、2016年に特電に来ていたアルバイトの学生さんが作ってくれた3D化のプログラムが使えないかどうかを試してみました。
この、新規ウィンドウ→3Dビューというのをやると3Dになるはずなのですが・・
そこで、彼が使っていたソースコードを引っ張り出してきてコンパイルしてみると、エラーがでてコンパイルできません。
どうやらfreeglut、glew、glfw、glm、stbというライブラリを使うようです。これらのライブラリが何なのか調べてみると、
- freeglut・・・ウィンドウの作成、マウス、キーボードの読み取り。古いらしい
- glew・・・OpenGL拡張をロードしてくれるライブラリ
- glfw・・・マルチプラットフォームで使えるツールキット
- glm・・・OpenGLで使う数学ライブラリ
- stb・・・画像やフォントを扱うライブラリ?
であるようです。
これらのライブラリを探してきてincludeのパスとlibのパスを通し、いざビルド!
無事にビルドが通りました。
MITOUJTAGに組み込むDLLだけではなく、彼はテストプログラムを用意していてくれたので、起動してみます。
うおおおー動いた!
マウスでグリグリ、シャカシャカ、グリグリ、シャカシャカ
拡大、回転、移動もできますね。
拡大していくとピン番号も出ます。3Dの画像に文字を重ねて表示することもできています。
しかし、いとも簡単にクラッシュします。
マウスでピンをクリックすると落ちるようです。
さて、このプログラムをデバッグして動くようにするか、それとも、このプログラムは参考程度にして全部自分で作り直すか、難しい選択です。
このプログラムを作った学生さんはOpenGLを使った物理シミュレーションの可視化みたいなことを趣味や研究で日ごろから作っていて、OpenGLに関しては素晴らしい腕前だったのですが、どうしてもコーディングの癖が強すぎるのです。
例えば、関数の引数をconst int &みたいに、const 参照にするという謎なことをしています。
2016年の当時「どうしてこうするの?普通にintで呼び出せばいいんじゃないの?」と聞いたのですが、「便利だから」とか「動作は同じだ」みたいなことを言って聞かないのです。__forceinlineみたいなのをつかいまくるし・・・害はないのですが、癖が強すぎます。
あと、変数の宣言でautoを付けたがる。
なぜだ・・?
とりあえず、何をやっているのか読み進めてみるとしましょうか。
2024.01.12
2024.01.11
2024.01.10
XILINXの10G Ethernet PCS/PMAを内部クロックで動かす
XILINXの10G Ethernet PCS/PMA(ten_gig_eth_pcs_mca)は外部から来る作動クロックで動かさなければ動きません。
MMCMで作った156.25MHzのクロックを無理やり使おうと思って、ten_gig_eth_pcs_mcaのサンプルデザインを書き換えてみたのですが、
[DRC REQP-56] connects_GTGREFCLK_ACTIVE: GTXE2_COMMON cell design_1_i/ten_gig_eth_pcs_pma_0/U0/ten_gig_eth_pcs_pma_core_support_layer_i/ten_gig_eth_pcs_pma_gt_common_block/gtxe2_common_0_i: Use of the GTGREFCLK is reserved for test purposes only. This has the lowest performance of the available clocking methods and can degrade transceiver performance. Note that GTGREFCLK use may be caused by driving a REFCLK with a BUFG.
というエラーが出てうまくいきませんでした。
エラーメッセージの最後に
Note that GTGREFCLK use may be caused by driving a REFCLK with a BUFG
というヒントのようなものが書き添えられています。
XILINX 7シリーズのUG476を見ると、
QPLLのソースにはGTGREFCLKという入力もあって、
CPLLやQPLLは、CLKSELを"111"にするとGTGREFCLKを選択できるようです。内部テスト用と書かれていますが、それで十分ですからぜひ使わせてください!!
というわけで、ten_gig_eth_pcs_pma_0_gt_common.vhdのソースでGTXE2_COMMONの部分を書き換えてQPLLCLKSELを111にし、gt0_gtrefclk0_common_inをGTGREFCLKに接続します。
これで論理合成が通るかと思ったのですが、結果は無慈悲なエラー。
先ほどと同じく、
[DRC_REQP-56] connects_GTGREFCLK_ACTIVE: GTXE2_COMMON cell・・・
が出てしまいます。
これを回避するにはXDCファイルに、
set_property SEVERITY WARNING [get_drc_checks REQP-56]
を書けばよいようです。
これで無事にFPGA内部のMMCMで生成した156.26MHzを使ってGTXを動かし、10GbEがリンクアップするようになりました。
xgmii_rxdとxgmii_rxcをにどういうパケットが流れているかを調べるため、MITOUJTAGを使ってパケットをキャプチャしてみました。最初に得られたパケットは
で、赤枠で囲った部分が0806になっていてARPでした。
パケットの先頭は D5555555555555FB ですがrxcが0x01でパケットの開始、rxcが0xffでデータなしという意味であるようですが、たぶん64b/66bの符号なので、1fcとか055と読むのだが正しいのかもしれません。
パケットの読み方を調べていたら、自分が過去に書いたセキュリティキャンプ用の資料が見つかりました。
https://www.tokudenkairo.co.jp/blogtemp/seccamp17_slide3.pdf
2024.01.09
特電の10Gビットイーサのサンプルデザインを再び動かしてみる
7年ほど前、特電がまだ学生アルバイトを雇っていたころに、そんな超優秀な学生さんが作ってくれた10Gbpsイーサネットの回路を動かしてみることにしました。10GイーサでARPやPINGを打ちまくるDOS専用回路みたいなものです。
サンプルデザインはここに置いてあるのですが、
https://www.tokudenkairo.co.jp/cosmok/10gbe.html
Vivado 2017で作られているので、とりあえずVivado 2022あたりで開いてアップデートします。
・・無事にアップデートはできました。
ちなみに超絶変態設計なのでIPコアではなくRTLモジュールでできています。ether_coreというモジュールの中に
で、実際に10GbEのメディアコンバータから光ファイバをCosmo-Kのボードに挿して動かしてみるとリンクアップしません。
Cosmo-Kのボードでループバックするとリンクアップしまs。
回路を調べてみると、10Gイーサのコアはクロックに156.25MHzを要するようです。
サンプルデザインの説明のページの写真を見ると、なんだか水晶を取り換えた痕跡があります。
特電が購入した部品のデータベースを見てみると、
あうあうあぅぁ・・・ 過去に1個だけ156.25MHzのMEMS発振器を買っている。たぶんこれだ。
ボード上の水晶はSATA用の150MHzで、速度が違いすぎるため市販の10GbEとリンクアップできないのでしょう。
ちなみに、Kintex-7のMMCMは100MHzから156.25MHzを作ることはできますが、XILINXのコア(10G Ethernet PCS/PMA)自体が、外部から来る差動クロック以外は受け付けてくれないのです。内部のBUFGじゃだめなのです。
2024.01.08
OpenOCDはなぜFTDIのドライバを使わずにlibusbやlibftdiを使うのか?
OpenOCDの使い勝手が悪い最大の欠点は、オープンソースにこだわりすぎてFTDIの操作にlibusbを使っていることです。
FTDIがせっかく用意しているドライバがプロプライエタリだからとかいうくだらない理由でlibusbを使っています。Zadigで置き換えちゃうとFTDIの他のやつが使えなくなるんですよね・・
レポジトリを調べてみたら、0.1.0、0.5、0.6.1くらいまではFTD2XXを使っていてたのですが、0.10.0-rc1からFTD2XXの痕跡が跡形もなくなっていました。
v0.10.0-rc1のNEWSにそう書かれています。ファイルの日付は2016年11月4日です。
GPL互換じゃないからという、実にくだらない理由で。
2009年6月にこういう↓議論がされています。
https://sourceforge.net/p/openocd/mailman/openocd-devel/thread/4A45EE86.4000208@op.pl/
こいつらそんなにプロプライエタリが嫌いなのかとうんざりしてしまいます。
完全に削除するのではなく、ユーザが自らの選択で復活できるしておいてほしかったですね。
復活させるパッチを作ろうかな!?
2024.01.07
OpenOCDをMinGWでコンパイルする
昨日はOpenOCDをWSL2で動くようにビルドしてみたのですが、ホストのWindows上で接続されたUSBをWSL2から認識させるにはかなり面倒くさい手続きが必要なので、Windowsのネイティブのアプリとしてビルドしたほうが便利だと気が付きました。
そこで、VisualC++でOpenOCDがコンパイルできるように移植をしようとしてみたのですが、すぐに挫折しました。contains_ofなどのLinuxカーネル系のマクロの扱いがGCCとVCで何か違うらしく、修正するのが馬鹿らしくなってきたためです。
↓こういうマクロです。
そこでMinGWを使うことにしました。MinGWやMSYSは古臭いという印象があったのですが、いろいろアップデートされているらしくmingw-w64とMSYS2という環境が最新のようです。
MSYS2は本家のページからダウンロードします。現時点での最新は msys2-x86_64-20231026.exe であるようです。
パッケージの管理はDebianやUbuntuではaptですが、MSYS2ではpacmanというのを使うそうです。世の中知らないことばかりです。
ダウンロードしたら
pacman -Syuu
をデータベースの更新がなくなるまで繰り返します。
この時点ではgccすら入っていないので、
pacman -S unzip bzip2 base-devel mingw-w64-x86_64-toolchain
pacman -S git
で基本的なツールをインストールします。
そして、
git clone git://git.code.sf.net/p/openocd/code openocd-code
でgitからクローンして、
cd openocd-code/
./bootstrap
をしてみますといろいろと足りないものが出てくるので、
pacman -S libtool
pacman -S pkg-config
pacman -S automake
pacman -S autoconf
で必要なツール類をインストールします。
これでbootstrapが通るようになりました。
ところが、MSYS2 MSYS(紫アイコン)の環境で./configureをしてみるとエラーで止まってしまいます。
どうやらMSYS2の環境にはいろいろあるらしく、
MSYS2-mingw64(青アイコン)の環境では無事にconfigureが通りました。
気にせずに青アイコンのMSYS2で続けます。
pacman -S mingw-w64-x86_64-libusb
pacman -S mingw-w64-x86_64-libhidapi
pacman -S mingw-w64-x86_64-libftdi
で、USBまわりのドライバと開発環境を入れて再びconfigureを行います
ちゃんとUSB関係のドライバも入りました。
これで、
make
ができるようになりました。
WSL2と比べるとものすごく時間がかかるのですが、仕方ないですね。
出来上がったopenocd.exeは、Windowsの環境に、libusb-1.0.dllと、ibftdi1.dllとlibhidapi-0.dllを持っていけば起動することが確認されました。
DependencyWalkerで見てみると、kernel32やMSVCRT.dllなどのDLLがあれば動くらしく、
使い勝手に関してはWSL2上で動かすよりも良いと言えます。
2024.01.06
WSL2で動作するOpenOCDを構築する
OpenOCDは基本的にLinuxのツールなので、Windowsで動かすにはWSLやMinGWといったLinux風の環境を作って動かします。今日はWSL2のUbuntu上でOpenOCDをビルドして動かすやりかたについてまとめます。
まず、
git clone https://git.code.sf.net/p/openocd/code openocd-code
で本家からopenocdをクローンしてきます。
これをコンパイルするわけですが、基本的には
cd openocd-code
に移動して
./bootstrap
./configure
make
sudo make install
でビルドします。
もしWSLをインストールした直後でgccやmakeといったツールが入っていない場合は、
sudo apt update
でapt自体を最新のものにしてから
sudo apt install libtool make
で、libtoolとmakeをインストールしてください。
bootstrapというのはaclocalやautoconfを実行するものですが、もし、以下のようなエラーが出てbootstrapが止まってしまったら、
configure.ac:32: error: Macro PKG_PROG_PKG_CONFIG is not available. It is usually defined in file pkg.m4 provided by package pkg-config.
pkg-configがない場合ですので、
sudo apt install pkg-config
でインストールしてください。
さて、bootstrapとconfigureを実行すると、どのようなケーブルが使えるように設定されたかのリストが出るのですが、最初は何も使えない状態になってしまっています。
そこで、
sudo apt install -y libftdi1-dev libhidapi-dev libgpiod-dev libcapstone-dev
を行います。
libftdi1-devがインストールされると、ほとんどのケーブルが使えるようになります。
libhidapi-devがインストールされるとCMSISDAP、Nu-Link、Cypress KitProgが使えるようになります。
Linux GPIO bitbangはlibgpiod-devです。
SEGGER J-LINKはconfigureで
./configure --enable-internal-libjaylink
とオプションを付けると使えるようになります。
Capstoneというのはケーブルではなく逆アセンブルのライブラリで、
sudo apt install libcapstone-dev
で有効になります。
このように、どのモジュールが使えるかを判断しているのが./configureスクリプトです。
すべてのモジュールが有効になると、次のようにすべてがyes (auto) と表示されるようになります。
そうしたら、
make
sudo make install
でインストールします。
さて、WSLの環境ではUSBデバイスがそのままでは使えません。パソコンに物理的に刺さったUSBデバイスは本体のWindowsが掴んでしまっていてWSLからは直接アクセスできないためです。
それを解決するのがusbipdとusbipというプログラムです。usbipdはWindowsで動くサーバプログラムで、usbipがWSL上で動くプログラムです。
詳細は
https://learn.microsoft.com/ja-jp/windows/wsl/connect-usb
https://usugi-not-usagi.hatenablog.com/entry/2022/05/22/015309
を参照してください。
まず、WSL上で
sudo apt install linux-tools-generic hwdata
sudo update-alternatives --install /usr/local/bin/usbip usbip /usr/lib/linux-tools/*-generic/usbip 20
を実行します。
それから、
cp /usr/local/share/openocd/contrib/60-openocd.rules /etc/udev/rules.d
で60-openocd.rulesというファイルをコピーします。60-openocd.rulesにはOpenOCDが対応するUSBのベンダID:デバイスIDがたくさん書かれています。
それから、
sudo service udev restart
sudo udevadm control --reload
で、udevを再起動して設定ファイルを読み直します。
Windowsの側ではPowerShellを管理者モードで開き、
usbipd list
でWSLに接続したいデバイスを探し(下の図では1-1)
usbipd --wsl bind -b 1-1
usbipd --wsl attach -b 1-1
でLinuxから接続できるようにします。
usbipd --wsl attach -b 1-1はUSBのデバイスを抜き差しするたびにやらなければならないので、面倒です。
こうして、WSL上で、USBを使うOpenOCDが利用できるようになります。
2024.01.04
OpenOCDの低レベルJTAGコマンドを試してみた
OpenOCDはフリーソフトで有名なJTAGアプリで、CPUのデバッグなどいろいろなことができます。また、TELNETで接続して低レベルコマンドで操作することもできるので外部のプログラムから遠隔操作、つまり、OpenOCDを組み込んだアプリを開発することもできるでしょう。
まず、OpenOCDを起動してjtag newtapコマンドでTAPを追加し、initしたときの波形を観察してみました。
下の図のような波形となりました。黄色はTCKで、緑がTMSです。
始まりの部分を拡大してみます。
少し長めのRESETシーケンスがあって、IDLE→SELECT_DR_SCAN→CAPTUREDR→SHIFTDRと遷移しているのがわかります。
最初の長いブロックの末尾は下の図のようになっています。
EXIT1→PAUSE→EXIT2→UPDATEDR→SELECT_DR_SCAN→SELECT_IR_SCAN→RESET→RESET・・と動いています。
2個目のブロックの末尾の部分は、IRをスキャンしています。おそらくここで、IRのステータスを読んでいるのでしょう。
このような感じでIDCODEとIRのステータスを読んでいると思われます。
さて、OpenOCDは低レベルのJTAGコマンドを発行することができるのですが、低レベルコマンドにはirscan、drscan 、jtag pathmoveがあります。使い方は次のような感じです。
irscan hoge.bs 0x0e
drscan hoge.bs 64 0xffffffffffffffff
jtag pathmove RESET RUN/IDLE DRSELECT DRCAPTURE DRSHIFT DREXIT1 DRUPDATE RUN/IDLE
まず、jtag pathmoveコマンドを試してみました。
jtag pathmove RESET
5クロックではなく7クロック分のTMS=1が出ています。優秀ですね。UltraScale+とかだと5発じゃだめなんです。それを狙ったのかどうかはわかりませんが。
次はRUNTEST-IDLEまで進めてみます。
jtag pathmove RESET RUN/IDLE
最後にTMS=0でパルスが出ています。
次はSELECT_DR_SCANまで動かしてみます。
jtag pathmove RESET RUN/IDLE DRSELECT
おや?RESETで止まっていますぞ。
TELNETの画面には
BUG: TAP path doesn't finish in a stable state
と出ています。
なんと、OpenOCDではRUN/IDLEとRESETとPAUSEとSHIFTステート以外には移動できないようです。このコマンドを実行しても実際にはRESETステートで止まるようです。
ただし、PAUSEステートや、SHIFTステートで止めることはできるようです。
それから、2週することもできないようです。
> jtag pathmove RUN/IDLE DRSELECT DRCAPTURE DRSHIFT DREXIT1 DRUPDATE RUN/IDLE DRSELECT DRCAPTURE DRSHIFT DREXIT1 DRUPDATE
wrong # args: should be "jtag pathmove wrong arguments"
いろいろ実験してみて、OpenOCDで出来ないことがわかってきました。
- RESET、IDLE、SHIFT、PAUSE以外のステートで止めることができない。irscanやdrscanの終了ステートに指定することもできない
- UPDATE-DRの後でSELECT-DR-SCANに直接戻るルートで遷移できない。必ずRUNTEST-IDLEを通らないといけない。ある種のCPUでは、RUNTEST-IDLEを通ると状態が変わってしまうものもあるので、そういうCPUのデバッグができない。
- RUN-TEST/IDLEでデータを送受信することができない。ある種のデバイスでは内部のステータスをRun-Test/Idle時に読みだすようになっているので、そういうデバイスが扱えない
- TMSを上げる下げるといった超低レベルの操作ができない。
これから作る新しいJTAGライブラリではOpenOCDをケーブル代わりにすることもできるようにする予定ですが、このあたりの操作がネックになりそうです。
2024.01.03
JTAGのセキュリティと暗号化スキーム
JTAGをネットワーク経由で通信する際の暗号化するスキームについて考えてみました。
何に対するセキュリティかというと、JTAGアプリへのリバースエンジニアリング対策としてのセキュリティです。新しく開発するJTAGライブラリはオープンソースにする予定のため、途中でどのようなデータが流れているかを容易に読み取られてしまいます。
そうなると、JTAGアプリからどのような通信が行われるかを観察して、デバイスの中のJTAG機能を推測されてしまいます。
このことはJTAGの内部機能を秘匿にしたいデバイスメーカーにとって脅威となります。一部のCPUメーカーがオープンソースのJTAGエミュレータに対して情報を提供しないのは、こういう理由があります。
物理的な出力にオシロをつないで解析されるのは仕方がないのですが、出力する前のデータとして取得されてしまうことは好ましくないようです。
そこで、物理的なJTAGケーブルとアプリとの間でデータを暗号化して保護することを考えます。
やり取りするデータはSVF的な
SDR 32 TDI (12345678) MASK (00000003) TDO (00000001)
のような感じですが、CPUのデバッグでは「フラグ待ち」という操作が頻繁に起きるので、
LOOP 100 WAIT 1us
SDR 32 TDI (12345678) MASK (00000003) TDO (00000001)
といった具合に、TDOからキャプチャされたデータが正しい値になるまでループして待つことができるようにします。
なお、USBを使ったJTAGケーブルは、USBの特性上、パケットを125μ秒のタイミングに合わせてしか送受信できないため、受信したデータが正しいかどうか判断して次のデータを送るということをするとそれだけで500μ秒くらい行ってしまいます。
例えばARMのデバッグではDPACCにアクセスするたびにACKフラグを確認しなければならないので、USB-JTAGで行儀よく作れば1つのMEM-APレジスタにアクセスするだけでミリ秒オーダーを消費してしまいます。
さらに、CPUのレジスタ一覧を見たり、CPUのメモリを見たりするととても遅くなります。そのため、フラグ待ちのような単純な条件分岐はできるだけケーブルのハードウェアにやらせたほうがいいわけです。
逆に、IF文やWHILE文、変数などの高度な機能が必要かというとそれは必要なさそうです。あと、ケーブルは非同期動作できたほうがいいでしょう。つまり、ある程度の操作コマンドをブロック化してケーブルに送り、TDOから受信したデータを順番にキューに入れて結果だけをまとめてアプリケーションに返せばよいわけです。
こういったデータの塊を暗号化して送れば、高速化とセキュリティが両立できるというわけです。
JTAGケーブルが公開鍵と秘密鍵のペアを持っていて、JTAGアプリ(エミュレータ等)は公開鍵を使ってデータを暗号化して送ります。JTAGケーブルとそのケーブルのための専用のドライバ(ソフトウェア)は、ケーブルメーカーが作るので秘密鍵を持っています。
こうすることで、オープンソース性を保ちながら、特定のJTAGケーブルだけで動作するJTAGアプリが作れるというわけです。
上の図で「ベースライブラリ」というのは何をやっているかというと、JTAGチェーンに複数のJTAGデバイスがつながっているときのスキャンビット列の生成とかTAPの操作とかBSDLの読み込みなどです。この部分はオープンソースです。ユーザが書き換えてprintfを仕掛けることもできるので、秘密性は全くありません。
ユーザアプリにくっついているJTAGユーティリティというのは、昨日のブログで書いたビット操作ライブラリなどです。ソース公開にはしますがソースで提供するので、ユーザアプリと一緒にコンパイルするので改ざんされて情報を盗まれるということは起きなくなります。
2024.01.02
C++で究極のビット列操作ライブラリを作成中
今年に入ってから新たなJTAGのライブラリを作っているのですが、言語はC++を採用しています。C++は演算子のオーバーロードができるので、ビット列というクラスを作れば、組み込みのレジスタ操作のための非常に柔軟なことができるのではないかと考えたためです。
組み込みのプログラムでは「レジスタの29bit目から21bit目に0x177を書く」みたいな操作が非常に多いのですが、普通はuint32_tとかの変数に対してビット演算やシフト操作で何とかすると思います。
しかし、JTAGではバウンダリスキャンレジスタというのが数百ビットから数千ビットになることもあり、uint32_tやuint64_tに納まりません。そのような長大なレジスタの任意のビットを設定したり取り出したりということができるライブラリを作りました。
まず、JTAGチェーンとデバイスを作ります。そして、バウンダリスキャンのDRにアクセスするためのdrというハンドルを取得します。
Chain* chain = new Chain(_T("hoge"));
Device* devive = chain->AddDevice(NULL, _T("dev1"));
BSReg *dr= devive->dr; // バウンダリスキャンレジスタへのハンドル
いままでのJTAGライブラリでは、自分のライブラリでも他の人のライブラリでも、長いビット列を扱うにはunsigned charの配列か何かを用意しておいて、setとかfillとかいう関数を作って何ビット目から何ビット目にどんな値を書き込むというような感じだったと思います。
本ライブラリではVHDL風の書き方でできるようにしたのと、ビット列を切り出した部分ビット列に対しても操作できるようにしたところ、非常に見通しの良い記述ができるようになりました。
dr->Length(36); // バウンダリスキャンレジスタDRの長さをセット
dr->TDI = 0x5555; // TDI(送信ビット列)に0x00005555を代入
Bits tdi = dr->TDI; // 送信ビット列の別名のハンドルを取得
tdi = 0x1234; // ビット列全体に整数値を書き込むことができる
tdi[32] = 0x1; // 特定のビットを配列のように操作できる
tdi(27 downto 24) = 0xf; // 部分ビット列に対してVHDL風の書き方で値をセット
tdi(57 downto 56) = 0x2; // はみ出しても安全
Bits tmp = tdi(27 downto 24); // 一部分を切り出したビット列を定義できる
別名を付けられるというのは、長いレジスタの一部分にある機能を狙って操作できるようにするためです。
例えば、32bitのレジスタの[27:24]がバンクセレクトである場合に、
Bits *banksel = tdi(27 downto 24);
と書けばいいわけです。で、この部分ビット列の別名を操作すると、元のレジスタも変化します。
結果の表示はDump()メソッドで行います。
tdi.Dump(); // ビット列全体の表示
tmp.Dump(); // 部分ビット列[27:24]の表示
tdi(32 downto 26).Dump(); // 7bitの部分切り出しを試す
printf("tdi(36 downto 26)=0x%08x\n", (int)tdi(32 downto 26)); // ビット列をintにキャストできる
結果は、
LEN=36 "0001 00001111 00000000 00010010 00110100" (0x1_0F001234)
LEN=4 "1111" (0xF)
LEN=7 "1000011" (0x43)
tdi(36 downto 26)=0x00000043
となりました。
次に、ビット列の長さを変えてみましょう。35bitというのはARMのDPACCを想定しているのですが、32bitに納まらない長さで下位3bitがコントロールになっているような状況でも見通しよく書けます。
dr->Length(35); // ビット列の長さを変えると、別名(この例ではtdi)の長さも変わる
tdi(2 downto 0) = 0x7; // 下3ビットに0x7をセット
tdi(35 downto 3) = 0x50000000; //上32bitに0x50000000をセット
tdi.Dump(); // Dumpメソッドで表示
printf("0x%llx\n", (uint64_t)tdi); // ビット列を64bit整数に変換してprintfで表示
結果の表示にはDumpメソッドを使う方法でもできますが、uint64_t や int などにキャストすれば自動的に基本型に変換されるようにしました。
LEN=35 "010 10000000 00000000 00000000 00000111" (0x2_80000007)
0x280000007
最後に、ビット列の長さを長くしてみましょう。
tmp = tdi(63 downto 32); // 元のビット列の[63:32]を切り出し
dr->Length(75); // 元のビット列の長さを75bitに変更
tdi.Fill(1); // ビット列を1で埋める
tdi(63 downto 32) = 0; // [63:32]を0にセット
tdi.Dump();
tmp = 0x12345678; // [63:32]に0x12345678をセット
tdi.Dump();
実行結果は、
LEN=75 "111 11111111 00000000 00000000 00000000 00000000 11111111 11111111 11111111 11111111" (0x7FF_00000000_FFFFFFFF)
LEN=75 "111 11111111 00010010 00110100 01010110 01111000 11111111 11111111 11111111 11111111" (0x7FF_12345678_FFFFFFFF)
となりました。
tdi(63 downto 32) = のようにして値を代入できるし、部分ビット列を使ってもアクセスできます。
C++で様々な演算子やキャスト、()などをオーバーライドした結果、非常にわかりやすくすっきりとしたビット列操作ライブラリができました。
2024.01.01
謹賀新年。今年はMITOUJTAGが生まれ変わります
あけましておめでとうございます。
今年はJTAGの開発をやり直したいと思っています。
私が未踏ソフトウェアに採択されてMITOUJTAGを開発したのが2003年ですが、あれから20年が経ち、MITOUJTAGのUI/UXは古くさくなってきました。
MITOUJTAGを作っていた西暦2000年ごろは、大きな画面にMDIアプリで小さなウィンドウがたくさん開き、プログラムのメニューにたくさんの項目とボタンが並ぶといったスタイルのソフトが多くありました。そして、一人のエンジニアが1台のPCにインストールして使うということが前提でした。私もそれに憧れてボタンを増やしてきました。
しかし今はそういう時代ではありません。ソフトウェアのUIからボタンがどんどん少なくなっていってたくさん並んだボタンやメニューは少なくなってきました。
ソフトウェアも売り切りではなく使った分だけ課金されるし、会社でも学校でも家でもスマホでも同一のデータにアクセスできるようになっています。ソフトのライセンスはPC単位ではなくユーザ単位に変わってきています。
EXEファイルで提供するというのも古くなっていて、セキュリティが厳しい会社では商用ソフトでさえインストールすることができません。そのためWebアプリというインストール不要の形態も出てきています。
多くのソフトでは体験版が用意されていて、使い続けたかったり、欲しいアドオンがあったら課金するようになっています。
ユーザはスマホでどこでも必要な情報にアクセスし、わからないことがあったらSNSで聞いて解決できます。
JTAGバウンダリスキャンについては20年間全く変わっていませんが、組み込みCPUのデバッグに関してはかなり環境が変わってきました。20年前はSHやARM7、MIPS、V850、XScaleという組み込みCPUが主流だったのに対し、今ではARMはCortexに、RISC-V、ESP32などのモダンな32bit CPUが台頭してきています。
この20年の間に起きたソフトウェアの提供形態やライセンス携帯、組み込みCPUを取り巻く環境の変化はこのような感じですが、MITOUJTAGもこの変化に追いつくために、全面的に一から作り直すことにしました。
それが、MITOUJTAGネクスト計画です。まだ構想段階で発表できない部分については一部黒塗りにしています。
20年前は、JTAGにアクセスするための物理層はプリンタポートか、Cypress+独自プロトコルか、FTDIがかろうじて出てきたころでした。JTAGの物理層を叩くのにも一苦労でした。
今では自分でJTAGケーブルを作らなくてもRaspiやM5Stackなど無線LANを内蔵したガジェットが安く使えるのでこれらを使ってJTAGケーブルを作ってしまうこともできます。USB-JTAGならFTDIのMPSSEが最高すぎます。また、XILINX Virtual Cableもひそかに人気です。OpenOCDのように様々なケーブルにすでに対応しているものをTCP/IP経由でJTAGケーブルがわりにするということもできそうです。
つまり、自分でJTAGケーブルを作らなくても身の回りにすでにある何かを使えるようにできればいいのではないかということです。
JTAGで必要になるケーブル接続というのも、身の回りにあるケーブルだけでなくTCP/IP接続を前提として作り、世界中のどこかにあるケーブルにつながるというのを最初から意識して作ることにします。
セキュリティーについては、「ハードウェアにアクセスするJTAGのデータを読み取られたり改竄されたりしない」というだけのセキュリティーではなく、CPUのデバッグやデバイスの書き込みなど、ベンダーが秘匿にしたい情報についてはリバースエンジニアリングが容易な一部のケーブルは使えないようにするといった、開発者から見たセキュリティを最初から考えます。
プログラムの開発環境も20年前とは変わってきていて、CやC++以外にもいろいろな選択肢が出てきました。若い人にはC#やPython、JavaScriptが人気です。
まずは、身の回りにある様々な物をJTAGケーブル化し、様々な言語で叩くことができるJTAGライブラリを作ることから始めようかと思います。
応援よろしくお願いします。
最近のコメント