/home/tnishinaga/TechMEMO

日々行ったこと、面白かったことを書き留めます。

起動しているLinuxマシンに適応されているDevice Tree Source(dts)を取得する方法メモ

Linux kernelが起動時に読み込んで使っているdevice treeをdts形式で確認する方法を毎回忘れるので備忘録として残します。

device treeを使っているアーキテクチャならどこでも使える方法のはず。

1. device-tree-compiler を入れる

device treeはdtbの状態で置かれているので、デコンパイルしてdtsに戻すためにdevice-tree-compilerをインストールします。

sudo apt-get -y install device-tree-compiler

2. linux kernelが読み込んでいるdtbデコンパイルしてdtsにする

kernelが起動時に読み込んで使っているdtb/sys/firmware/fdt にあるので、これをデコンパイルしてdtsに戻します。 /sys/firmware/fdt の読み込み権限がroot以外になかったので、sudoをつけています。

sudo dtc -I dtb -O dts /sys/firmware/fdt -o ~/devicetree.dts

3. 読む

後は読むだけです。

以上。

1600円のロジックアナライザとsigrokでJTAGを見る

現在、フルスクラッチJTAGをしゃべるプログラムを開発しています。 このプログラムのデバッグ用にJTAG信号を解析できるロジックアナライザを探したところ、うまく使えるものが見つかったので、備忘録として残しておきます。

sigrok

JTAG信号を解析できる信号解析アプリケーションとして、sigrokというオープンソースソフトウェアがあります。 今回はこれを使ってJTAG信号を解析しようと思います。

sigrokの持っているデコーダ一覧は以下参照です。

sigrok.org

sigrok対応デバイスを買う

sigrokを使うには対応ハードウェアが必要なので、探します。 sigrokがサポートするハードウェア一覧は以下。

sigrok.org

この中で最も安価そうなのは、1000円から2000円程度で買えるSaleae Logic(FX2LP系)クローンデバイスです。 私が買ったもののお値段は1600円でした。付属品や購入先によっては1000円程度の物もあるようです。

www.amazon.co.jp

sigrokをM1 Mac(Big Sur)で動かす

Mac OSX用のsigrokは以下からダウンロードできますが、私の環境ではエラーが出て起動すらしませんでした。

sigrok.org

そのため、ちゃんと起動するsigrokをビルドするhomebrewスクリプトを作成しました。

一部HEAD指定必須なので、以下のコマンドを実行してインストールしてください。 (WIPなので修正したほうが良い点があればPRお願いします!)

github.com

brew tap tnishinaga/sigrok
brew install libsigrok sigrok-firmware-fx2lafw
brew install --HEAD libsigrokdecode
brew install sigrok-cli
brew install --HEAD pulseview

sigrokを使ってみる

Saleae Logic cloneをPCに接続し、terminalからpulseviewを実行してGUIアプリを起動します。

起動したら画面上のデバイス選択部分をクリックし、fx2lawドライバを選択してデバイススキャンを行い、出てきたデバイスを選択します。

f:id:tnishinaga:20210913195751p:plain

f:id:tnishinaga:20210913235829p:plain

次に画面上からデコーダー選択ボタンを押し、JTAGを選択したあと、ピンの割当を行います。

f:id:tnishinaga:20210913200051p:plain

f:id:tnishinaga:20210913200144p:plain

あとは画面左上のRunボタンを押してJTAGの信号を流すだけです。 うまく信号を捕まえられれば、JTAGのステートを確認したり、送受信したデータのhexがみられて開発効率が向上すること間違いなしです。

f:id:tnishinaga:20210913200249p:plain

VSCode remote SSH extension でつないだRaspberry Pi上でrust-analyzerを動かす方法メモ

計算リソースやデバイス接続の関係から、最近は開発を手元のMacで行うのでなく、リモートサーバーにVSCodeのRemote ssh extension機能を使って接続して行っています。

今回はリモートサーバーにRaspberry Piを利用した際にrust-analyzerが動かず困ったので、その解決方法をメモします。

使ったRaspberry Piは以下

  • Board
  • OS
    • RaspberryPi OS(32bit, buster)

なぜ動かないか

rust-analyzerのバイナリが32bit arm向けに配布されていないため、VSCodeのMarketplaceからインストールできるrust-analyzerは動作しないようです。

github.com

どうすれば動くか

Raspberry Pi上でrust-analyzerをビルドしてVSCodeに読み込ませれば動きます。

以下の作業を Raspberry PiにRemote SSH extensionで接続したVSCodeのターミナル上 で実行してください。 (rustup等のインストールは完了しているものとします)

sudo apt-get update
sudo apt-get -y install nodejs npm
git clone https://github.com/rust-analyzer/rust-analyzer
cd rust-analyzer
cargo xtask install

最後にVSCodeを再起動すればrust-analyzerが動作します。

f:id:tnishinaga:20210909043626p:plain

iPad pro 2018 11inch WiFi modelを購入しました

2019年3月末に2018年モデルのiPad Pro 11inchとその周辺機器を購入しました。

それから約1ヶ月ほど使ってみて、買った周辺機器やアプリなどが色々あるので、感想を書いていこうと思います。

忙しい人向け

  • お金の余裕があればiPadはCellerモデルをおすすめ
  • 保護フィルムは紙質感のものをiPadと一緒に買ってつけよう
  • アプリの自動更新は真っ先に止めよう
  • iPad上でコーディングする際の良い方法があれば教えてください

目的

iPadでやりたかったことは以下です。

  • スムーズなPDFの閲覧およびPDF内へのメモ書き込み
  • 2000ページ越えPDFファイルの閲覧(ARMのアーキテクチャマニュアル等)
  • キーボード付きのリモートSSH作業
  • キーボード付きの簡易コーディング作業
  • 簡単なお絵かき(ホワイトボードの代わりくらい)

この希望を満たせないかなと色々買ったりしてみました。

購入したハードウェア

購入したハードウェアの一覧は以下です。

  • iPad Pro 2018 11inch(WiFi model)
  • iPad smart keyboard folio(11inch, US)
  • (+ Apple care)
  • Apple Pencil(2nd Gen)
  • Apple USB-C - 3.5 mmヘッドフォンジャックアダプタ
  • iPad 2018 11.1インチ用フィルム/ペーパーライク/反射防止/ケント紙タイプ
  • Anker PowerLine II USB-C & USB-C 2.0 ケーブル (1.8m)

iPad Pro 11inch(2018 model)

iPad Proには11inchと12.9inchモデルがあります。 今回はお値段と大きさの都合から11inchを購入しました。

大きさによる不満はほぼ無いです。あるとすれば、iPadは2つまでアプリを同時に画面に表示して使えるようになっているので、この機能を使ってドキュメントを書きながらsshで作業するなどしているときはすこしだけ大きな画面が欲しくなります。

お値段の都合から今回はWiFiモデルを購入しましたが、こちらはCellerモデルを購入した方が良かったと今は考えてます。iPhoneを持っているのでWiFIモデルでもテザリングでうまく使えると考えてましたが、取り出してiPhoneテザリングで接続して...という一手間が増えるのは不便です。

加えて、iOSWiFiデータ通信はアプリ別通信量等がみられないので、通信量の多いアプリを見つけて設定を変えてということがうまくできないのが大変です。 iPadは接続先がテザリングiPhoneであることは知っているはずですが、データ通信量の手加減をしてくれないようで、わたしは4/1の初日に、ほとんど使ってないiPadに1.5GBものデータ通信量をもぐもぐされました。 前述の通りWiFi通信ではアプリごとのデータ通信量がわからないので正確な原因特定はできていませんが、アプリの自動更新を止めたところ今のところは通信量が落ち着いてます。 iPadを買ったら最初にこの設定を行ないましょう。

smart keyboard folio 11inch(US)

iPadのカバー兼物理キーボードです。 カバーだけのものに比べ重さと値段が結構増えますが、わたしは購入して良かったと考えてます。

というのも、iPadの標準のスクリーンキーボードは慣れてないせいもあると思いますが結構打ちづらく、ミスタイプが辛いのです。加えて公式のスクリーンキーボードは日本語と英語の切り替えボタンの反応が良くないのと矢印キーが無いのも大変です。スクリーンだと選択とコピペがやりづらいのもあります。

そもそもアプリによっては文章の一部選択ができなかったり、うまくコピーが働かなかったりしてキーボードがあってもうまくいかない場面が出てきたり色々あるのですが、そういう不満を挙げ続けていくと「あれ?わたしの欲しかったのはiPadではなくMacBookだったのでは?」と考えてしまうので「iPadでやるのはiPadでできる軽量なことまで。それ以上はパソコンでやる」というふうに割り切って使う必要があると思います。

話を戻しましょう。このキーボードカバーもファンクションキーがないなど気になるところはありますが、本体とセットで持ち歩けていつでもサッとキーボードが使えるのは、よく文章を書く人にとっては便利だと思います。

キーのサイズはエンターキー付近の幅がすこし狭くなっていますが、概ね問題ありません。 なお、12.9inchモデルのキーボードはその辺りの幅も通常サイズのようです。

Apple Pencil(2nd gen)

実は2世代目のpencilが使いたいというのが、最近出たiPad miniではなくproを購入した理由になります。

2世代目はマグネットでipadにつながり、勝手に充電されるのが便利で良いです。 ただし、そんな強くくっついているわけではないので、いつ落とすか心配です。 iPadをカバンに入れて持ち運ぶ際はペンだけカバンのポケットに入れたりしています。

ペンを収納する機能のついたケースもあるようです。

Apple care

延長保証です。

今回購入した2018年モデルはネット上の「曲がりやすい」という噂や、実際にバッグに入れて使ってるだけで曲がった人の報告ツイート(URL失念)などを見かけていたので、絶対に入った方が良いと考えました。

Apple製品はapple care等の補償もりもりにして買うものだよ」と友人のろまのふさんが言っていたのも理由の一つだったりします。

最近のデバイスは修理も大変ですし、壊してから数万の修理代をかけるよりは最初に1万円払ったほうがお得かなと考えてます。

Apple USB-C - 3.5 mmヘッドフォンジャックアダプタ

iPadリズムゲームするなら必須です。千円くらいで買えます。

www.apple.com

iPad 2018 11.1インチ用フィルム/ペーパーライク/反射防止/ケント紙タイプ

iPadの表面を紙っぽくする保護フィルムです。ペンシルでの書き心地が良くなります。 iPadと一緒に購入して開封後すぐにつけるといいと思います。

完全に予想外だったのですが、これをつけると滑りがよくなってiPadでのリズムゲームが捗ります(リズムゲームやるために買ったわけじゃないんですが...iPadリズムゲームやるの楽しい...)。

www.elecom.co.jp

Anker PowerLine II USB-C & USB-C 2.0 ケーブル (1.8m)

標準のケーブルは短いので買いました。充電だけでしか使ってませんが問題なく使えてます。

www.ankerjapan.com

インストールしたソフトウェア

インストールして良かったアプリを抜粋してご紹介。

Blink Shell: Mosh & SSH Client

有料買い切りのSSHクライアントです。2400円だったはず。

SSH Jumpが使えるとのことで買いましたが、以下に書かれているようにProxyCommandにncを使った設定を入れないとうまく動きませんでした。設定を入れれば動くのでそのまま使っています。

ssh -o "RemoteCommand=nc %h %p 2>/dev/null" -q  JUMP_SERVER

github.com

Port forwardingもできます。ただしiOSの仕様でバックグラウンドにいったアプリは10分で消されてしまうからか、しばらくすると接続が切れてしまいます。ちょっと残念。

LastPass

クラウドで管理するタイプのパスワードマネージャーです。

今までKeePass + Dropboxで運用していたのですが、Dropboxのbtrfs非対応化とiOS用KeePassクライアントで使いやすいものがなかったので乗り換えました。

iPad上でもパスワード生成してアカウント作成などが手軽にできて便利です。

今は無料版を使っていますが、Yubikeyを買ったら有料版にしてYubikeyと連携して使っていこうと思います。

GoodNotes 5

PDFを読んだり注釈を入れたり、手書きノートを作ったりできる買い切り有料アプリです。 他人からのおすすめで購入しました。 今のところ2000ページ越えのマニュアルも読めるし検索できて便利に使えてます。

Paper by WeTransfer

slankさんのおススメを受けてかった、ホワイトボード的に使えるお絵かきアプリです。月額課金の有料アプリです。 有料にすると一部を選択してcutして移動などができるようになり大変便利になります。

PWEditor

テキストエディタです。Google DriveDropboxなどのファイルが読めます。 Markdownをとりあえず読みたいときに使ってます。 120円で広告を外せます。やすい。

足りないもの

コードエディタ

大規模なコードを見たり書いたりするにはPWEditorは力不足を感じるので、良いエディタを探しています。

リモートサーバーにSSHで入ってファイルを編集するといったことができるととても嬉しいのです。 現在はVPSSSHで入ってemacsで開発をしていますが、パソコンではvscodeで開発を行なっているので、できればGUIなエディタが欲しいです。

coder.comのサービスにはとても期待しており課金して利用する予定でしたが、現在は課金プランもなくなり、無料版では重くて使えないという状況です。 クラウドサービス上に認証ありでサクッと建てる方法があれば自分で建てて使いたいのですが、知識不足でできていません。ご存知の方がいれば教えてください。

Rockstor をインストール時に暗号化してリモートから鍵入力できるようにする話

NASLinuxディストリビューションのRockstorを使ってお家のNASを作り直したので、そのときにやったことを備忘録として書き残しておきます。

再構築の目的

私は以前からRockstorを入れたNASサーバーを運用していたのですが、以下の点が気になっていたのでOSごとインストールし直しを行いました。

  • UEFIでなくBIOSブートでインストールされている
  • Data diskが暗号化されていない
  • data diskの鍵を入れるOS diskも暗号化されていない
  • RAIDを組むためにData diskを2枚指したのにRAIDを組めていない

再構築の主目的は以下の2点になります。

  • RockstorのOSの入っているdisk(以降OS disk)含めて暗号化する
  • Dataを入れるdisk(以降data disk)をRAID1にする

なお、RockstorをUEFIブートするようインストールする件に関しては、途中で発生した問題をすべて解決できなかったので諦めました。詳細は最後におまけで記します。

暗号化の話

インストール

最近のインストーラーはとても親切なので、指示に従っているだけでDisk暗号化をしつつOSのインストールができました。 わからないことがあっても、RockstorはRHELディストリビューションをベースとしているので、FedoraCentOSのドキュメントを参考にすれば良いでしょう。

ディスク暗号化のリモート解除

さて、ここが私がこの備忘録を書いたきっかけになります。

インストール後、実際に起動してみて困ったのが起動時のディスク暗号化解除方法です。 Rockstorでディスク暗号化を行いつつインストールを行うと、起動時にディスク暗号化を解除するためのパスコード入力を求められます。 しかし、うちのサーバーマシンは基本的にディスプレイとキーボードを外して運用していることに加え、頻繁に電源をON//OFFするので、毎回起動時にパスワード入力を求められると不便です。

これを解決するために、はUSBメモリに鍵を入れ、起動時に自動で復号する方法やSSH経由でリモートから鍵入力をする方法がありますが、今回は後者を試してみました。この方法を使うメリットとしては、復号鍵が対象から分離されることと、USBメモリなど追加で必要なものがないので気軽に試せる点があるでしょう。

基本的な作業手順は以下のサイトのStep6以降を進めるだけです。

https://www.vultr.com/docs/install-and-setup-centos-7-to-remotely-unlock-lvm-on-luks-disk-encryption-using-ssh#Step_6__Install_Dracut_Crypt_SSH

こちらも参考になります。

https://github.com/dracut-crypt-ssh/dracut-crypt-ssh

Rockstorのマシンにrootでログインした後、dracut-crypt-sshパッケージを入れます。

sudo yum install wget -y
sudo wget -O /etc/yum.repos.d/rbu-dracut-crypt-ssh-epel-7.repo https://copr.fedorainfracloud.org/coprs/rbu/dracut-crypt-ssh/repo/epel-7/rbu-dracut-crypt-ssh-epel-7.repo
sudo yum install dracut-crypt-ssh -y

次に/etc/default/grubを開いてgrubのエントリを修正し、起動時にIPアドレスを得るように設定します。

アドレス取得をDHCPで行う場合は、GRUB_CMDLINE_LINUX="crashkernel=autord.luks.uuid= の間に rd.neednet=1 ip=dhcp を入れれば設定完了です。

static IPを割り当てたい方は以下を参照してください。

https://wiki.archlinux.jp/index.php/Dm-crypt/%E7%89%B9%E8%A8%98%E4%BA%8B%E9%A0%85#root_.E3.81.AA.E3.81.A9.E3.81.AE.E3.83.91.E3.83.BC.E3.83.86.E3.82.A3.E3.82.B7.E3.83.A7.E3.83.B3.E3.81.AE.E3.83.AA.E3.83.A2.E3.83.BC.E3.83.88.E8.A7.A3.E9.99.A4

設定後のGRUB_CMDLINE_LINUXの行はこんな感じになります。

GRUB_CMDLINE_LINUX="crashkernel=auto rd.neednet=1 ip=dchp rd.luks.uuid=luks-xxxx rhgb quiet"

設定が完了したら以下のコマンドでgrubのconfigを生成します。

sudo grub2-mkconfig -o /etc/grub2.cfg

次にinitramfs内に入れるSSHの設定を記入していきます。

/etc/dracut.conf.d/crypt-ssh.conf を開いて以下を追記します。

dropbear_acl="/etc/dropbear/keys/authorized_keys"
dropbear_ecdsa_key="/etc/dropbear/keys/ssh_ecdsa_key"
dropbear_rsa_key="/etc/dropbear/keys/ssh_rsa_key"

ファイルを保存したら、ここで設定した場所に新しいSSH鍵を生成します。 この鍵は起動時にディスク復号パスワードを受け取るためのSSH通信に利用されます。

sudo mkdir -p /etc/dropbear/keys/; sudo chmod /etc/dropbear/keys/
sudo ssh-keygen -t ecdsa -f /etc/dropbear/keys/ssh_ecdsa_key
sudo ssh-keygen -t rsa -f /etc/dropbear/keys/ssh_rsa_key
sudo chmod 400 /etc/dropbear/keys/*_key; sudo chmod 444 /etc/dropbear/keys/*.pub

次に /etc/dropbear/keys/authorized_keys を開き、ディスク復号鍵を送るための、接続元マシンの公開鍵を記述します。ここで記述する鍵はrsaの公開鍵である必要があります。dropbearを使う場合はed25519鍵でアクセスできないようです。ここ、ドハマりポイントでした。

最後にdracutコマンドでinitramfsを生成して再起動すれば設定完了です。

sudo dracut -f && sudo reboot

再起動後、サーバーにユーザーroot、ポート222で接続し、console_authコマンド実行後にディスク復号のパスワードを入れればOSが立ち上がります。

なお、このアンロックの手順も自動化できるようなのですが、とりあえず今日はここまでで。詳しい話は以下を参照。

https://github.com/dracut-crypt-ssh/dracut-crypt-ssh#34-unlocking-using-the-unlock-command

data disk暗号化

data diskの暗号化は公式のドキュメントがあるのでこちらを参照して作成してください。

http://rockstor.com/docs/luks/luks.html

RAIDを組む場合は先にディスク暗号化を済ませてからpool割当のところで設定すれば作れます。詳しくは以下参照です。

http://rockstor.com/docs/pools-btrfs.html#redundancy-profiles

おまけ: RockstorがUEFIブートでインストールできない話

結論から話せば、少なくとも2019年2月ごろのRockstorはUEFIブートからインストールができませんでした。以下理由を記しますが、メモをとっていなかったため誤りが含まれるかもしれません。

まず、インストーラーのISOをそのままUSBメモリに書き込むとUEFIブートしません。

これはfatでフォーマットしたUSBメモリにISOの中身を書き込むと回避できますが、今度は立ち上がったKernelがrootfsをマウントできず失敗します。

これはどうやらrootfsのラベル指定がUSBメモリのラベルを指していないことが原因のようで、grub.cfgを書き換えてUSBメモリのラベルで参照できるようにすると解決します。

この修正でインストーラーが立ち上がるところまで進みますが、インストール途中にエラーが起きて先に進めない問題が発生したため、解決を諦めました。

おしまい

おしまい。

Chisel + PYNQでLチカ(お正月FPGAあそび)

お正月なので普段やらないことをやろうと思い、買ってから2年ほど放置してしまっていたPYNQ-Z1を使ってFPGAを触ってみました。

RISC-Vで遊びたい」という気持ちと「FPGAをさわってみたい」という気持ちがあったので、「PYNQでRISC-Vを動かしてみる」ことを最終目標にしました。 しかし、これではお正月休みだけでは終わらないので、このお休みの間は「PYNQでChiselを使ってLチカする」のをゴールとしてやってみました。

今回はそこで何やったかのメモ的な記事です。

なお、私はFPGAScalaもChiselもほぼ初挑戦です。

Vivado setup

PYNQはDigilent社の作ったFPGAボードです。

www.pynq.io

PYNQにはXilinx社のZynqというFPGAが乗っているので、Xilinx社のVivadoという開発環境で開発を行います。 以下のサイトからアカウントを作ってインストールします。WebPackライセンスを選択すればいろいろ制限はありますが無料で使えます。

japan.xilinx.com

インストールが終わったら以下からPYNQ向けのボードファイルをもらってきてインストールします。

https://github.com/cathalmccabe/pynq-z1_board_files/raw/master/pynq-z1.zip

$ wget https://github.com/cathalmccabe/pynq-z1_board_files/raw/master/pynq-z1.zip
$ unzip pynq-z1.zip
$ sudo cp -r pynq-z1 /tools/Xilinx/Vivado/2018.3/data/boards/board_files

PS + PL でLチカ

環境設定が終わったので、組み込み版HelloWorldであるLチカをやってみます。

PYNQボード向けの入門ドキュメントは少ないので、だいたい同じ構成のZyboというボード向けの初心者用ドキュメントを参考に、一部読み替えつつLチカをしてみます。

Getting Started with Zynq [Reference.Digilentinc]

PYNQボードに乗っているZynqというFPGAチップには、ARMのCPUコア(PS: Processing System)とFPGA(PL: Programmable Logic)が1つのチップに乗っています。 今回参考にするドキュメントの例では、PL部に用意したGPIOをPS部のプログラムから制御して、スライドスイッチの状態に応じてLEDを点灯させるというものになります。

Zybo向けに書かれたこのドキュメントをPYNQで用いるための変更点としては、PYNQにはスライドスイッチが2つしか無いので、手順4.3で設定するスイッチをswts_2bitsに設定する必要があります。

PL部のみでLチカ

次にPYNQをPS部を用いず普通のFPGAボードとして利用する方法が知りたいので、以下のサイトを参考にPL部のみでLチカをしてみたいと思います。

qiita.com

こちらのドキュメントもZYNQ向けなので、「ピンアサインをする」のところでクロックなどが接続されているピンがPYNQと異なります。そのため、以下のマニュアルを参考に変更を加えます。

https://reference.digilentinc.com/_media/reference/programmable-logic/pynq-z1/pynq-rm.pdf

「11 Clock Sources」と「12 Basic I/O」を読むと、クロックソースとLEDのピン配置が以下の表のようになっていることがわかるので、設定します。

Parts FPGA Pin
125MHz Clock H16
LED LD0 R14
LED LD1 P14
LED LD2 N16
LED LD3 M14

回路がうまく動けば、1秒ごとにLEDが4つとも点滅します。

Chiselを使ったLチカ

RISC-VのコアはChiselという、ScalaDSLとして実装された言語を用いて書かれています。 このChiselの書き方を学ぶため、まずはこのChiselを使って先程のPL部だけで行うLチカの回路を作ってみたいと思います。

まずChiselから実際のボード上で動く回路を作るまでの流れが知りたかったので調べてみます。 Chiselのコードをビルドすると、中間表現を経由してVerilogのコードが得られます。実際のボード上で動かすためにはこの生成したVerilogをvivadoに取り込んでピンアサインなどを設定してビルドする必要が有るようです。

詳細についてはmskysphinzさんのブログ記事が参考になりました(ありがとうございます)。

msyksphinz.hatenablog.com

次にプロジェクトの開始の際には、chisel-templateというリポジトリをベースに行うのが良いようです。

github.com

chisel-remplateにはGCDを求める回路が最初から書かれています。 最初は簡単のためメソッド名等はそのままに中身だけ書き換えてLチカのコードを書いていきます。

src/main/scala/gcd/GCD.scala

Verilogで書いていたLチカのコードをChiselで書き直します。 Clockは暗黙の引数として有るようなので、書かなくて良いようです。 onoff変数の値をLED4つに反映させる方法がわからなかったので、LED0にだけonoffの値を入れるようにしています。

// src/main/scala/gcd/GCD.scala

package gcd

import chisel3._

/**
  * Compute GCD using subtraction method.
  * Subtracts the smaller from the larger until register y is zero.
  * value in register x is then the GCD
  */
class GCD extends Module {
  val io = IO(new Bundle {
    val outputLED     = Output(UInt(4.W))
  })
  //  parameter CNT_1SEC = 27'd124999999;  // 125MHz clk for 1sec
  val CNT_1SEC = RegInit(124999999.U(27.W))
  //  reg [26:0] cnt = 27'd0;
  val cnt  = RegInit(0.U(27.W))
  // reg onoff = 1'd0;
  val onoff  = RegInit(false.B)

  // if (cnt == CNT_1SEC) begin
  when(cnt === CNT_1SEC) {
    // cnt <= 27'd0;
    // onoff <= ~onoff;
    cnt := 0.U
    onoff := ~onoff
  } .otherwise {
    // cnt <= cnt + 27'd1;
    cnt := cnt + 1.U
  }

  // assign LED = {onoff, onoff, onoff, onoff};
  io.outputLED := onoff
}

src/test/scala/gcd/GCDUnitTest.scala

回路のテストコードを書きます。

サンプルではpokeを使って回路に入力を設定しているものがよく見られますが、この回路は入力がクロックしか無いので、pokeで入力設定を行わなくても良いようです。

stepを進めるとクロックが入力されてカウンタが上がっていきます。 今回125MHzでLEDの出力を反転させるので、stepで125,000,000進めた後にexpectを使って出力が変わっているかをチェックします。

class GCDUnitTester(c: GCD) extends PeekPokeTester(c) {
  private val gcd = c

  // return 0 when clk counter == 0
  expect(gcd.io.outputLED, 0)

  // return 0 when clk counter == 1
  step(1)
  expect(gcd.io.outputLED, 0)

  // return 1 when clk counter == 124999999
  step(125000000 - 1)
  expect(gcd.io.outputLED, 1)

  // return 1 when clk counter == 0
  step(1)
  expect(gcd.io.outputLED, 1)

  // return 0 when clk counter == 124999999
  step(125000000 - 1)
  expect(gcd.io.outputLED, 0)
}

src/test/scala/gcd/GCDMain.scala

そのままもテストはできますがVerilogファイルの出力ができなかったので、chisel-wikiを参考にMainメソッドに1行追加してVerilogのコードをビルドできるようにします。 以下のドキュメントを参考に、出力のための処理を追記します。

Frequently Asked Questions · freechipsproject/chisel3 Wiki · GitHub

object GCDMain extends App {
  iotesters.Driver.execute(args, () => new GCD) {
    c => new GCDUnitTester(c)
  }

  // 追記
  chisel3.Driver.execute(args, () => new GCD)
}

テスト

tutorialのREADME.mdに書いてあるとおりに以下のコマンドを実行するとテストが走ります。

$ sbt 'testOnly gcd.GCDTester -- -z Basic'

うまくいけばこんな感じのログが出てきます。

$ sbt 'testOnly gcd.GCDTester -- -z Basic'                                                                                                                                                                                                                                                                                                                                                                                  
[info] Loading settings from plugins.sbt ...
[info] Loading project definition from /home/tnishinaga/projects/chisel/chisel_ledblink/project
[info] Loading settings from build.sbt ...
[info] Set current project to chisel-ledblink (in build file:/home/tnishinaga/projects/chisel/chisel_ledblink/)
[info] Compiling 1 Scala source to /home/tnishinaga/projects/chisel/chisel_ledblink/target/scala-2.11/classes ...
[warn] there was one feature warning; re-run with -feature for details
[warn] one warning found
[info] Done compiling.
[info] [0.005] Elaborating design...
[info] [2.191] Done elaborating.
Total FIRRTL Compile Time: 512.2 ms
Total FIRRTL Compile Time: 201.5 ms
file loaded in 0.388108123 seconds, 13 symbols, 9 statements
[info] [0.002] SEED 1546794998929
test GCD Success: 5 tests passed in 250000005 cycles in 118.643097 seconds 2107160.15 Hz
[info] [118.613] RAN 250000000 CYCLES PASSED
[info] GCDTester:
[info] GCD
[info] Basic test using Driver.execute
[info] - should be used as an alternative way to run specification
[info] using --backend-name verilator
[info] running with --is-verbose
[info] running with --generate-vcd-output on
[info] running with --generate-vcd-output off
[info] ScalaTest
[info] Run completed in 2 minutes, 3 seconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[info] Passed: Total 1, Failed 0, Errors 0, Passed 1
[success] Total time: 135 s, completed Jan 7, 2019 00:01:42 AM

Verilogコードの生成

Verilogのコード生成は以下のコマンドで行えました。 このコマンドがスマートな方法かはわかりませんが、動いたので良しとしています。どなたか良い方法をご存知の方は教えてください。

$ sbt 'test:runMain gcd.GCDMain --target-dir buildstuff --top-name GCDMain'

うまくビルドが終わればbuildstuffディレクトリ以下にGCDMain.vというファイルができているはずです。 機械が生成したコードなので、先程の人間の作ったコードに比べるとちと読みづらいです。

module GCD( // @[:@3.2]
  input        clock, // @[:@4.4]
  input        reset, // @[:@5.4]
  output [3:0] io_outputLED // @[:@6.4]
);
  reg [26:0] cnt; // @[GCD.scala 19:21:@9.4]
  reg [31:0] _RAND_0;
  reg  onoff; // @[GCD.scala 21:23:@10.4]
  reg [31:0] _RAND_1;
  wire  _T_13; // @[GCD.scala 25:12:@11.4]
  wire  _T_15; // @[GCD.scala 27:14:@14.6]
  wire [27:0] _T_17; // @[GCD.scala 29:16:@18.6]
  wire [26:0] _T_18; // @[GCD.scala 29:16:@19.6]
  wire [26:0] _GEN_0; // @[GCD.scala 25:26:@12.4]
  wire  _GEN_1; // @[GCD.scala 25:26:@12.4]
  assign _T_13 = cnt == 27'h773593f; // @[GCD.scala 25:12:@11.4]
  assign _T_15 = ~ onoff; // @[GCD.scala 27:14:@14.6]
  assign _T_17 = cnt + 27'h1; // @[GCD.scala 29:16:@18.6]
  assign _T_18 = cnt + 27'h1; // @[GCD.scala 29:16:@19.6]
  assign _GEN_0 = _T_13 ? 27'h0 : _T_18; // @[GCD.scala 25:26:@12.4]
  assign _GEN_1 = _T_13 ? _T_15 : onoff; // @[GCD.scala 25:26:@12.4]
  assign io_outputLED = {{3'd0}, onoff}; // @[GCD.scala 33:16:@22.4]
`ifdef RANDOMIZE_GARBAGE_ASSIGN
`define RANDOMIZE
`endif
`ifdef RANDOMIZE_INVALID_ASSIGN
`define RANDOMIZE
`endif
`ifdef RANDOMIZE_REG_INIT
`define RANDOMIZE
`endif
`ifdef RANDOMIZE_MEM_INIT
`define RANDOMIZE
`endif
`ifndef RANDOM
`define RANDOM $random
`endif
`ifdef RANDOMIZE
  integer initvar;
  initial begin
    `ifdef INIT_RANDOM
      `INIT_RANDOM
    `endif
    `ifndef VERILATOR
      #0.002 begin end
    `endif
  `ifdef RANDOMIZE_REG_INIT
  _RAND_0 = {1{`RANDOM}};
  cnt = _RAND_0[26:0];
  `endif // RANDOMIZE_REG_INIT
  `ifdef RANDOMIZE_REG_INIT
  _RAND_1 = {1{`RANDOM}};
  onoff = _RAND_1[0:0];
  `endif // RANDOMIZE_REG_INIT
  end
`endif // RANDOMIZE
  always @(posedge clock) begin
    if (reset) begin
      cnt <= 27'h0;
    end else begin
      if (_T_13) begin
        cnt <= 27'h0;
      end else begin
        cnt <= _T_18;
      end
    end
    if (reset) begin
      onoff <= 1'h0;
    end else begin
      if (_T_13) begin
        onoff <= _T_15;
      end
    end
  end
endmodule

FPGAでの動作確認

PL部でのLチカを参考に、VerilogのコードをコピペしてVivadoでビルドを行います。 Chiselはreset用のpinも欲しがるので、私はとりあえずスライドスイッチのついているM20を設定しました。

PYNQにうまく書き込めれば、LEDのLD0だけがチカチカするはずです。

おもったこと

最後に、やってる途中に思ったことをつらつらと。

  • ChiselのまえにScala(基本文法とsbtつかったビルド方法)を勉強したほうが良さそう
  • Verilogを使った普通のFPGA開発も本一冊分くらいはやったほうが良さそう
  • RISC-Vの実機が触りたいなら10万出してhifive unleashed買うか、マイコンで良ければArty FPGAボードかHiFive1を買ったほうがよさそう

UEFIの呼び出し規則について

UEFIの呼び出し規則について調べたので忘れないようにメモしておきます。

呼び出し規則とバイナリフォーマット

UEFIのアプリケーションは以下の点が一般的なLinuxのアプリケーションと異なるため、Linux環境上でUEFIアプリをビルドするには解決の必要があります。

  • 関数呼び出し規則(calling convention)が異なる(x86, x86_64の場合)
  • 実行バイナリのファイル形式がPEフォーマット(UEFI Specification Version 2.5 Errata A pp.18 2.1.1 UEFI Images参照)

私のようにx86以外をターゲットとしてビルドする際は後者の解決が重要ですが、今回は前者について調べたことを記します。

呼び出し規則への対応

前者の呼び出し規則とは、関数を呼び出す際のレジスタの使い方などを決めているものです。 規則にはどのレジスタに引数を入れて渡すか、どのレジスタに戻り地を入れて返すかなどが決められているため、この規則が異なる関数をそのまま呼び出すことはできません。 この規則の違いがx86系のプロセッサ上で動作するUEFIアプリを作る際に問題となります。

x86 Linux向けにアプリをビルドすると、呼び出し規則はSystem V ABIに定められたものが使われます。 一方、UEFIではMS-ABIやMicrosoft x64 calling conventionと呼ばれるWindowsで使われる呼び出し規則を利用しているため、Linux等からUEFIアプリをビルドするためにはこの呼び出し規約の差異を解決する必要があります。 (UEFI Specification Version 2.5 Errata A pp.32 2.3.4.2 Detailed Calling Conventionsを参照。直接MS-ABIとは書いてはない)

私の知っている限りでは、この差異を解決する方法が2つあります。

  • MS-ABIでビルドする
  • ラッパー関数で呼び出し規則を修正する

MS-ABIでビルドする

前者はコンパイラにms-abiを用いるオプションを指定したり、attribute で指定することで解決を行います。

gcc.gnu.org

gnu-efiではコンパイラがms-abiに対応している場合、defineを使ってEFIAPIをつけた関数に __attribute__((ms_abi)) を適応してms-abiで呼び出すようにしています。

ラッパー関数で呼び出し規則を修正する

後者はコンパイラがms-abiを利用できない場合に利用される方法です。 gnu-efi ではuefi_call_wrapperというラッパー関数(中身はマクロ)を利用して、引数の数に合わせて引数の順序を入れ替える関数を選んで使用しています。

Linuxでも同じようなことをやっているようです(efi_call関数を参照)。

linux/efi_stub_64.S at 6f0d349d922ba44e4348a17a78ea51b7135965b1 · torvalds/linux · GitHub

なお、ARMやその他アーキテクチャでは基本的にLinuxと同様のABIを用いているので、ABIの変換作業は必要ありません。 実際にgnu-efiのラッパー関数も変換を行っていないようです。

gnu-efi / Code / [fc5af9] /inc/aarch64/efibind.h

以上です。 PEフォーマットの件は次回書きます。