かずの不定期便ブログ

備忘録代わりに書きます

FPGA Sipeed Tang Nano 9Kでフォトビューワーになりそこなったものを作りました

FPGA Sipeed Tang Nano 9Kでbmp画像を表示させる

フォートビューワ

はじめに

今回は、ここ数回で行ったTang Nano 9KでHDMI表示、SDカードへのアクセス、PSRAMへのアクセス、picorv32の実装の総まとめの意味合いでSDカードに記録されたBMP形式の画像をHDMIモニタへ表示させることにします。フォトビューワー的なやつです。
残念ながら、フォトビューワーと呼ぶにはあまりに速度が遅くて恥ずかしい出来上がりになってしまっているのですが、一旦ココで区切りとしてブログへまとめたいと思います。原因は突き止めているので、記事の最後に記載します。

フォトビューワー(のようなもの)の要件

  1. HDMIの解像度と同じ画素数(1280x720)の24bit BMPのみ表示
  2. SDカードのルートフォルダのbmpフォルダに格納されている上記条件にヒットするBMP画像のみ表示
  3. 上限は10枚まで
  4. ファイルはロングファイルネームのものを扱える(日本語不可)
  5. bmpフォルダの中の画像ファイルをすべて表示したら最初のファイルへ戻って表示する

といった、かなり縛りのあるものを作成しました。(これ以上のものは別の目的になってしまう気がしたので)
今回の作成物は、あくまで"HDMI表示、SDカードへのアクセス、PSRAMへのアクセス、picorv32の実装"で得た知見でアプリっぽいものを作る事が目的です。

先の仕様になった理由を簡単に書くと、

  1. 解像度変換プログラムを書くのが面倒くさい。picorv32の性能的に苦しい
  2. SDカードを他の用途でも使いたいのでフォルダを分けて分別したかった
  3. ファイル名を格納するメモリがたりない。。。
  4. 正直メモリ観点からロングファイルネームは切り捨てたかったのですが、今回借用した画像のファイル名を変更するのが面倒でした。(ココに写真を載せる関係上、画像は個人であれば無償、申請不要で利用可能なプロ生ちゃんを使わせていただいています)
  5. フォトビューワーだから無限ループしたい

HDMI表示の流れ

PSRAMをVRAMとして使って、DMAを使ってPSRAMから画像データを読み出し、HDMI表示用のデータを生成します。
VRAMとなるPSRAMへの画像データの書き込みはCPU(PicoRV)が行います。
また、CPU(PicoRV)が画像データをSDカードからbmpファイルを読みだします。

ハード構成図(SDカード→PSRAM)

PSRAM書き込み側制御回路

SDカード読み出しから、PSRAMへデータが移動するルートのブロック図が下の絵になります。

PSRAMへの画像の書き込み

Fifoへ一旦CPUからのデータを書き込んで、64byte溜まったところでDMAを使ってPSRAMへ64byte分、バースト転送で書き込んでいます。

本方式は書き込みしか出来ません。

Fifoメモリは、CPUからは1個の32bitのデータレジスタとして見えているだけで、fifoのライトアドレス自体はハードウエアで自動生成しています。

fifoは64byte x 4段 = 256byteのdualポートメモリになっており、DMA転送中でもCPUからのライトを阻害しない、CPUからのライト中でもDMA転送を阻害しない作りになっています。

fifoへのライトアドレス、fifoからのリードアドレスを比較してfifoのfull,empty判断を行っています。(実際には非同期クロックな関係なので、問題が発生しないようにクロック乗換を行ったうえで比較しています)

CPUのfifoへのライト動作はno-wait,つまり1clockで出来る様にしています。
fifoからの読み出しクロックが早いので、fifoがフルになる(=CPUへwaitをかける)事はないはずですが、CPUからのライト動作はno-waitなので、fifoの64byteバースト読み出し間に隙間が出来ると、クロック周波数差を埋めてフルになることもあるかもしれません。

HDMIへ送信するデータはRGB888=24bit/pixelですが、PSRAMへのアクセス帯域を減らすため、CPUでRGB565=16bit/pixelへデータ量を削減して書き込みます。HDMI送信時にRGB888へ変換します。

fifoがフルにならなければ、CPUの動作クロックが37.125MHz,バス幅が32bitなので、37.125x4=148Mbyte/sec程度の能力があります。画像データは1280x720サイズなので 1280 x 720 x 2byte=約1.8Mbyte, 148/1.8=80frame/secの性能がある計算なので、CPUからの書き込みがボトルネックになることはないです。

ボトルネックになるのはSPIによるSDカードからの読み出しです。SPICLKは約9.28MHzなので、1.16Mbyte/secの性能です。これだと1280 x 720 x 3 bytesの読み出しに2.4secかかる事になるので、フォトビューワーとしてはギリギリくらいの性能です。
SPIクロックをあげる事を後ほど考える事にします。(しかしボトルネックは全然違うところにあることが後で分かります...)

ハード構成図(PSRAM→HDMI)

HDMI表示制御回路

PSRAMからDVITXへの転送のブロック図が下記になります。

PSRAM→DVITXへの転送

こちらの回路も転送単位64byteの4倍の量のfifoを間に置いてデータの非同期解決と転送が滞らないようにしています。

図では端折ってますが、fifoのフル状態を検出してfifoへの書き込み制御(停止)を行っています。

DVI_TXへ同期信号(vs,hs)に合わせてデータを供給し続ける必要があるため、fifoがempty状態になることは許されません(emptyになると表示が乱れます)。

動作の様子


www.youtube.com

terminal表示↓

  ____  _          ____         ____
 |  _ \(_) ___ ___/ ___|  ___  / ___|
 | |_) | |/ __/ _ \___ \ / _ \| |
 |  __/| | (_| (_) |__) | (_) | |___
 |_|   |_|\___\___/____/ \___/ \____|

        On Lichee Tang Nano-9K
This is modified firmware. add SD access.

Start vram init: *** timer value=0_ffb8ad ***
End vram init: *** timer value=0_58c0f3a ***
reached max file num.
filename: bmp/kei_and_conoha_R.bmp
xsize=1280, ysize=800
filename: bmp/2011-12_1920x1080_freddie104_R.bmp
xsize=1280, ysize=720
Start write image to vram. *** timer value=0_621534f ***
End write image to vram. *** timer value=0_b4a1acd0 ***

1枚の画像を表示するのに80秒近くかかってしまっています。
途中4倍速表示にしています。右下にある時計の秒針を見ていただくのがよいかと
フォトビューワじゃなく、画像をどこかにゆっくりとロードしている様を見ている感じになってます。

SPIを使ったSDカードからの画像ロードは最速で2.4秒程度だと見積もっているのですが、30倍以上の時間がかかっています。SDカードからのロードが本当にボトルネックなのでしょうか?

TangNano9Kのロジックアナライザ機能を使って、バスの動作を確認してみることにします。

picoバスアクセス

信号名 description
clk_p picorv32のクロック
u_picorv32/mem_valid picorv32のバス
u_picorv32/mem_ready picorv32のバス
spimemxip_valid spiフラッシュのバス
spimemxip_ready spiフラッシュのバス

お分かりいただけるでしょうか?u_picorv32/mem_ready信号の"L"区間が27サイクル程度もあるバスアクセスがあります。これはSPIフラッシュへのアクセスになり、SPIフラッシュに格納されている命令コードのフェッチに非常に時間がかかっている(28サイクル)事を示しています。

真のボトルネックは命令読み出しにありました。まずはここを改善しないと性能向上が出来ないです。

原因が判明したことで、フォトビューワー作成は一区切りにしたいと思います。
ここを改善するには内蔵フラッシュとpicorv32の間にSRAMのバッファを入れるなど対策があるのですが、やりたいことと別な気がするので、次の新たなネタを探す事にします。

回路規模

今回の回路の規模を記載します。

Resource Usage Utilization
Logic 4784(3931 LUTs, 721 ALUs, 22 SSRAMs) / 8640 55%
Register 2464 / 6771 36%
--Register as Latch 0 / 6771 0%
--Register as FF 2464 / 6771 36%
BSRAM 12 / 26 46%

まとめ

内蔵フラッシュへのアクセスで28cycle程度かかる。うーむ。

まとめとタイトルの関連性がイマイチない感じですが、今回の記事は今までTangNano9K関連で扱ってきた総まとめのつもりだったのですが、新たな発見があったため、今回この発見をまとめにしてみました。

次回

未定です。
Tang Primer 20K(Nanoではないです。いずれ買いますけど。。。) を実は入手しているので、回路規模の大きいこちらへ軸足を移そうかなと思っています。
いつになるか分かりませんが新たなアイデアか発見があれば気長に書いていきたいと思います。
Risc-Vやりたいけど、難しいですねぇ。ソフト知見が足らなさすぎます。

今回の参考資料

  • SDカードへアクセスするためのSPIのverilogコードは以下を利用。CSがないSPI_Master.vを利用しました。
    Copyright (c) 2019 russell-merrick github.com

  • fatfsのコードは以下を参照しました
    qiita.com

  • Tang Nano 9Kのpicotinyシステム
    github.com

  • 今までの記事です。(こうしてみると初回のHDMI表示から1年近く経過している。。。なかなか時間が取れなくて)

spend-carefree.hatenablog.com

spend-carefree.hatenablog.com

spend-carefree.hatenablog.com

spend-carefree.hatenablog.com