かずの不定期便ブログ

備忘録代わりに書きます

FPGA Sipeed Tang Nano 9KでDualPortメモリの作成方法

FPGA Sipeed Tang Nano 9KでDualPortメモリの作成方法

BSRAMとは

BSRAMとはBlockSRAMの略で、FPGA内にRAMがハードマクロとして埋め込まれているのですが、これの事をBSRAMと言います。LUTで作成することに比べて、高速動作可能なメモリが実現可能です。

BSRAMの推論が上手くいかないRTL記述

DualPortMemoryを作ろうとしたのですが、RTLからうまくBSRAMを推論してくれない、というかRTL記述自体を合成ツールが受け付けてくれない場合があったのでここに記載します。
結論を先に記載しますとRTLの記述が悪いだけで、推論は可能です。ちょっと苦労したので、文書として残します。
DualPortMemoryとはclockが2本(clkA,clkB)あり、どちらのclockでもライト、リードが行えるメモリの事です。
Aポートのライト側のみを抜き出したRTL記述は以下になります。

    always @(posedge clkA) 
        if (enaA) begin
            if(weA[0]) 
                ram_block[addrA][7:0] <= dinA[7:0];
            if(weA[1]) 
                ram_block[addrA][15:8] <= dinA[15:8];
            if(weA[2]) 
                ram_block[addrA][23:16] <= dinA[23:16];
            if(weA[3]) 
                ram_block[addrA][31:24] <= dinA[31:24];
        end

データ幅が32bitのメモリでバイトライト機能付きです。 このRTLを合成すると以下の様なメッセージが出て、合成が出来ません。

ERROR (IF0003) : Cannot infer "ram_block" due to multiple write clocks("ソースコードのファイル名":32)

ram_blockに対し複数のライトクロックがあるので、合成できないとの事。確かに通常のFFであれば、その通りですというERRORメッセージなわけですが、このRTL記述はDualPortメモリを作りたいが故なので、そうは言われてもという感じです。。。
とりあえずRAM合成に関するドキュメントを探しました。IDEのインストールフォルダにある
UG285-1.3.3J_Gowin BSRAM & SSRAMユーザーガイド.pdf
によると、デュアルポートモードという記述があります。clock 2本の独立したポートが扱えるRAMをサポートしていることが分かります。ただ、1点気になる文章があり、
GW1N-9/GW1N-1S/GW1NR-9/GW1NS-2/GW1NS-4 はデュアルポートモードをサポートしません。
とあります。使えないチップがある事が気になります。tang nano 9kは"GW1NR-9C"なので非該当だと思っていいのだろうか。最後のCの有無の違いって。。。と疑問がよぎります。
合成可能か確かめるべく6章に記載があるように「IP の呼び出し」で直接BSRAMをインスタンスしてみる事にしました。
なんのエラーもなく普通に置けてしまいました。
一点気になる点があり、端子一覧や3章にあるタイミング図を見ると、バイトライト機能はありません。全ビットをライトするか否かを指示するようです。
今回32bit幅でバイトライトイネーブル付を実現したいので、8bit幅のRAMを4個インスタンスする事で実現しました。
またBSRAMは16kbit単位で利用されるので、16kbitに満たない容量のRAMであっても16kbit分使用されることに注意が必要です。今回1KByte(8Kbit)のRAMが必要だったのですが、先に記載したように32bit幅を8bit幅単位でRAMを分ける必要があったので1/4の2Kbitになってしまいます。つまりBSRAM1個当たり、1/8の容量(2Kbit)しか利用しないことになり非常にもったいない事案が発生してしまいます。それでいて貴重なBSRAMが4個も消費されてしまいます。

BSRAMを直置きするとSIMが出来ない

BSRAMを直置きしましたので、そのままではSIMが出来ません。なので、SIM時は自分が作成したRAMモデル(つまり合成可能だと思っていたRTLを使う)を使い、合成時はインスタンスしたRAMを利用するという方法にします。これらはdefineで切り替えることにしました。

RTLから合成でBSRAMが推論されない理由が判明

IDE付属の「SUG550-1.5J_GowinSynthesisユーザーガイド.pdf」というドキュメントを見てみます。
なんとここにBSRAMを推論するためのHDLコードサンプルが記載してあります。このサンプル通りにDualPortメモリのRTLコードを書いてみます。
合成すると、なんと、なんの問題もなく通ってしまいました。
最初の自分のコードと比較するとバイトライト機能の有無の差分があります。最初の自分が作成したコードからバイトライト機能を取るだけで合成が可能でした。
そうなんです。BSRAMにはバイトライト機能はないので、RTL記述もそのような機能を付けてはいけません。
試してはいないですが、シングルポートメモリであってもおそらく同様です。今までシングルポートメモリでバイトライト機能を付けてなかったので、BSRAMへうまく割り付けてくれてたのかな?と思います。
すごく遠回りしましたが、分かってみると単純でした。この文章も結論だけ記載すれば、短くまとめられますが、なんか悔しいので経緯も含めだらだらと書いてしまいました。

結論。DualPortメモリでもHDLコードからBSRAMは推論可能です!!

DualPortメモリのデータ幅は16bitまでにとどめるか、もしくはBSRAM1個当たりの最大容量16Kbitを使うように心がける

最後に、今回の調査の中で気が付いた注意点について記載します。
「UG285-1.3.3J_Gowin BSRAM & SSRAMユーザーガイド.pdf」の「表2-1 BSRAM の構成モード一覧」には以下の様に構成可能なbit,wordの組み合わせがあります。

BSRAMの構成モード一覧.
上段の16Kbitsの容量であれば、bit,word構成を変更しても容量は16Kbitあるという表なのですが、赤丸で囲ったところ、一部例外があります。
DualPortメモリに限り32bit幅のメモリは実現できないという点です。
これを無理やり作るとどうなるか試したのが下記のスクショになります。
DualPortメモリを32bit幅で作成
DPB Usageのところを見ていただきたいですが、2個のBSRAMが使われてしまっています。容量は256word x 32bit ですので、8Kbitsです。BSRAM 1個で16Kbitまで使えるはずなのにです。
というもったいない事が発生するので、DualPortメモリは16bitまでで使うのが良いと思います。 因みに、word方向を2倍、データ幅を16bitにして同容量のRAMを作成すると下記のようになり、BSRAMの利用が1個に抑えられています。
16bit幅で作成
これらの事から類推できることは32bit幅のメモリを作成する場合には16bit幅のメモリを横に並べて実現しているのだと思われます。

ただ、32bit幅のメモリが必要な場面は多いと思うので(DualPortメモリはそんな必要な機会はないとは思うのですが)、その場合には16bit幅を横に置く対処を行うしかないわけで、どのみち避けては通れないということになります。
後、蛇足ですが、32bit幅の1Kwordのメモリを作成した場合には2個のBSRAMで構成してくるので、とにかく32bit幅にすると、2倍の物理容量(半分は無駄)が持ってかれるというわけではありません。

まとめ

BSRAMを推論させたいならバイトイネーブルのRTLを作成してはいけない。 DualPortメモリは他の種類のメモリと異なり16bit幅が最大です。 PSRAMの使い方は、理解しました。
これで8MBもの大容量メモリを手に入れたので、picorv32を使った大きなアプリが作れそうですね。

次回

PSRAMをVRAMとして使い、SDカードからbmp画像を読みだしてHDMI表示させるビットマップ画像viewerでも作成しようかなと思います。(前回も書きましたが)
実はほとんどできているのですが、SDカードから読み出し、PSRAMへの書き出し速度が遅すぎて、viewerと言えますか?これって感じなので、キャッシュメモリを実装して高速化出来ないか?とか(キャッシュメモリでは高速化に寄与しないことが作ってから判明←今ココ)取り組んでいたため、遅くなってます。

今回の参考資料

GOWINのIDEをインストールすると同時にインストールされるマニュアル SUG550-1.5J_GowinSynthesisユーザーガイド.pdf
UG285-1.3.3J_Gowin BSRAM & SSRAMユーザーガイド.pdf