Cyclone V GT FPGA Development KitでHello, world!
Cyclone V GT FPGA Development Kitにはキャラクタ液晶(LCD)が付いているので何かと便利に使えそうだ.
さぁ適当なIPをインポートしてサクッとHello, world!と思ったら,そのような便利なIPは(少なくとも無償では)無いらしい.えーI2C通信自分で書くの・・・.今までマイコン等でI2Cの何かを使うときはライブラリの上から適当に使ってきたので,信号の細かな動きは全く知らない.仕方ないので調べながら書いた.この界隈そんなに人少ないのか・・・.
資料
公式のユーザーガイドとリファレンスマニュアル.
LCDのデータシート
http://www.newhavendisplay.com/specs/NHD-0216K3Z-NSW-BBW-V3.pdf
環境
プロジェクトファイル一式:https://github.com/SenriYoshikawa/CycloneVGTFPGADevelopmentKit/tree/master/LCD
仕様
実装
コードすべての説明を書くとかなりの量になりそうなので,特に詰まったところだけメモがてら書き残す.
inoutピン
SCLとSDAは場合により入力にも出力にもなり得る.そんなものどうやってVerilogで書くのだと思い調べたところ,inout宣言した入出力ポート(wire)に対して,条件によりregか'z'(ハイインピーダンス)をassignすればよいらしい.
こんな感じ.
assign i2c_sda = sda_valid ? sda_reg : 1'bz;
自分(FPGA)はマスターなので,出力したいときはsda_validをHighにしてsda_regに書き込み,スレーブの応答を見たいときはsda_validをLowにしてi2c_sdaを読めば良い.
SCLのためのクロック生成
I2Cの詳細については丁寧に解説しているサイトがたくさんあるのでそちらに譲る.FPGAで実装しようとしてまず困ったのは,I2Cのクロック信号(SCL)がせいぜい数百kHzまでしか対応しないらしく,PLLではそこまで遅いクロックを作ることができないことだ.
仕方がないので以下のような何とも言えないモジュールを書いた.50MHzクロックでカウンタを動かし,50KHzのクロックを生成している.一応動いているので良しとしているが,より良い方法ご存じの方教えてください・・・.
ちなみに,なぜリセットのエッヂ検出をしているかというと,リセット中にカウントが止まるとクロックも止まり,50Kで同期しているレジスタをリセットできないからだ.ここも少し嵌った.
module Clk50K( input clk_50M, input rstn, output reg clk_50K ); reg [1:0] rstn_buf; reg [9:0] count_50K; // rstn_buf always @(posedge clk_50M) begin rstn_buf <= {rstn_buf[0], rstn}; end // count_50K always @(posedge clk_50M) begin if(rstn_buf == 2'b10) begin count_50K <= 0; end else if(count_50K >= 499) begin count_50K <= 0; end else begin count_50K <= count_50K + 10'd1; end end // clk_50K always @(posedge clk_50M) begin if(rstn_buf == 2'b10) begin clk_50K <= 1'b0; end else if(count_50K >= 499) begin clk_50K <= ~clk_50K; end else begin clk_50K <= clk_50K; end end endmodule
SCLとSDAの同期
SCLはSDAに対するクロックで,通常のデータ転送においてはSCLがLowの間にSDAを書き換える同期回路でよく見る動作なのだが,スタートシーケンスではSCLがHighの間にSDAをLowに,ストップシーケンスではSCLがHighの間にSDAをHighにしなければならない.
この動作を実現するために,2ビットのひたすらインクリメントするレジスタを設け,この上位ビットをSCLに繋いだ.
reg[1:0]scl_reg | scl |
---|---|
2'b01 | 0 |
2'b10 | 1 |
2'b11 | 1 |
2'b00 | 0 |
これによりSCLがHighとLowのそれぞれで2クロックあるので,SCLがHighの間にSDAを操作したければ,scl_regが2'b10の時に代入すればよい.通常のデータ代入はscl_regが2'b00か2'b01の時に行えばよい. しかし後で見たら自分でも首を傾げそうな実装だ.みんなどうしてるんだろう.
その他
あとは初めに思ったほどややこしいことはなく,愚直に書いたら動いた.
ピン配置
LCDのピンがどこにつながっているかはリファレンスマニュアルのP2-25に書いてある.
LEDはデバッグ用に3つ繋いでいるがなくても動く.
書き込み・動作確認
例によってデバイスチェーンに気を付けて書き込んで動作確認.
動いた.うれしい.