FC2カウンター FPGAの部屋 2013年05月
fc2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

カメラ・インターフェース用AXI4-Stream IPの作製2(pixel_fifo の生成)

カメラ・インターフェース用AXI4-Stream IPの作製1(仕様の検討)”で同期FIFOが必要になったので、pixel_fifo を生成した。

pixel_fifo には、RGBそれぞれ8ビットの合計24ビットと、TLASTのために1ビットの合計25ビットを用意する。TLASTのアサートのために同期FIFOのデータの出口で1クロック分、ピクセルデータを遅延することにした。

それでは、CoreGen で作る pixel_fifo の設定を見ていこう。
Cam_AXI4_Stream_IP_1_130530.png

・同期FIFOで、Block RAMを選択した。
Cam_AXI4_Stream_IP_2_130530.png

・25ビット長、512深度を選択した。First-Word Fall-Through を選択した。
Cam_AXI4_Stream_IP_3_130530.png

・almost_full, almost_empty, overflow, underflow を選択した。
Cam_AXI4_Stream_IP_4_130530.png

・リセットは同期リセット(SRST) に変更した。(2013/05/30)
Cam_AXI4_Stream_IP_5_130530.png

・ここもデフォルト値
Cam_AXI4_Stream_IP_6_130530.png

・Block RAMのリソースは、18K BRAMを1個使用する。これで、Generateした。
Cam_AXI4_Stream_IP_7_130530.png

これで、pixel_fifo が生成された。
  1. 2013年05月30日 05:13 |
  2. AXI4-Stream IPの作製
  3. | トラックバック:0
  4. | コメント:0

カメラ・インターフェース用AXI4-Stream IPの作製1(仕様の検討)

カメラ、ビデオ表示カスタムIPを AX4-Stream に変更2(カメラ・インターフェースIP)”で仕様を検討したカメラ・インターフェース用のAXI4-Stream IPを作製しようと思う。(カテゴリをAXI4-Stream IPの作製に変更しました)
(AXI4-Streamでは、仕様上、TVALIDはTREADYを待ってはダメだという指摘を受けたので、カメラ・インターフェース用AXI4-Stream IPの仕様を変更します。教えて頂いた ikvzm さんありがとうございました。)

下にカメラ・インターフェース用のAXI4-Stream IPの仕様を示す。

1.カメラ・インターフェース用AXI4-Stream IPは、FIFOを持たない。FIFOはAXI VDMA の C_S2MM_LINEBUFFER_DEPTH (”LogiCORE IP AXI Video Direct Memory Access v5.04a Product Guide PG020 December 18, 2012”の86ページの”C_S2MM_LINEBUFFER_DEPTH”参照)でFIFO深度を決定できるFIFOを使用する。つまり、AXI VDMAの s2mm_buffer_full がアサートされたらデータ転送が間に合わずにエラーということだ。これは、カメラからくるデータの転送速度は決まっているので、これ以下のストリーム転送しか出来ない場合は当然エラーとなる。

1.改 カメラ・インターフェース用AXI4-Stream IPは同期FIFOを持ち、少なくとも同期FIFOがEMPTYでなくなった状態まで、カメラ・データが溜まった後で、TVALIDをアサートする。

2.1.の前提があるので、AXI4-Stream のTREADY の途中でのディアサートは想定しない。最初にカメラのフレームを転送する最初には、TREADY を確認しアサートされていたら、AXI4-Stream でカメラのフレームの画像データを転送する。TREADY がアサートされていない場合は、そのフレームはAXI4-Stream で転送しない。

2.改 同期FIFOを入れることになったので、AXI4-Stream のTREADY の途中でのディアサートにも問題なく対応できる。ただし、待てる範囲はFIFOの容量による。

3.AXI VDMAの s_axis_s2mm_aclk は入力で、カメラ・インターフェース用のAXI4-Stream IPからクロックを入力することができる。カメラからのPCLKを入力することで、カメラ・インターフェース用のAXI4-Stream IPのクロックドメインはPCLKの1つとすることができる。

4.カメラからのデータはRGB565 なので、2クロックで1つのピクセルデータが転送される。よって、AXI4-Stream のTVALID のアサートは s_axis_s2mm_aclk の2クロックについて1回となる。


・最初に、”AR# 37425 12.3 EDK、12.3 ISE - カスタム AXI IP コアの作成方法”から ar37425.zip をダウンロードする。

・ar37425.zip を解凍して、ar37425/axi_stream フォルダをテンプレートとして、カメラ・インターフェース用のAXI4-Stream IP (mt9d111_inf_axi_stream.vhd) を作る。
mt9d111_inf_axi_stream.vhd の entity を下に示す。(2013/06/01:修正)

entity mt9d111_inf_axi_stream is
  generic(
        -- Master AXI Stream Data Width
        C_M_AXIS_DATA_WIDTH : integer range 8 to 1024 := 24
    );
  port (
        s2mm_aclk    : out std_logic;
        s2mm_prmry_reset : in std_logic;
        s2mm_fsync        : out std_logic;

        -- Master Stream Ports
    --    m_axis_aresetn : out std_logic;
        m_axis_tdata   : out std_logic_vector(C_M_AXIS_DATA_WIDTH-1 downto 0);
        m_axis_tstrb   : out std_logic_vector((C_M_AXIS_DATA_WIDTH/8)-1 downto 0);
        m_axis_tvalid  : out std_logic;
        m_axis_tready  : in  std_logic;
        m_axis_tlast   : out std_logic;
        
        init_done        : in std_logic; -- PS部の初期化終了
        
        -- MT9D111 Camera Interface
        pclk_from_pll    : in    std_logic;    -- PLLからMT9D111のxck に出力するクロック
        pclk            : in     std_logic;    -- MT9D111からのピクセルクロック入力
        xck                : out    std_logic;    -- MT9D111へのピクセルクロック出力
        href            : in     std_logic;
        vsync            : in    std_logic;
        cam_data        : in    std_logic_vector(7 downto 0);
        standby            : out    std_logic;    -- STANDBY出力(ディスエーブル、0固定)
        pfifo_overflow    : out    std_logic;    -- pfifo overflow
        pfifo_underflow    : out    std_logic    -- pfifo underflow
    );

end mt9d111_inf_axi_stream;


これで、カメラ・インターフェース用のAXI4-Stream IPを作っていこうと思う。クロックドメインを変換する非同期FIFOは実装しないので、比較的簡単に実装できそうだ。
  1. 2013年05月29日 05:02 |
  2. AXI4-Stream IPの作製
  3. | トラックバック:0
  4. | コメント:4

カメラ、ビデオ表示カスタムIPを AX4-Stream に変更2(カメラ・インターフェースIP)

最初の記事は、”カメラ、ビデオ表示カスタムIPを AX4-Stream に変更1(使用するIPの検討)
前の記事は、”カメラ、ビデオ表示カスタムIPを AX4-Stream に変更1(使用するIPの検討)

まずはカメラ・インターフェイスIPをVideo In to AXI4-Stream IPを使用して作ることにした。
ここで問題となるのは、カメラからの信号には、HSYNC(水平同期信号)とVSYNC(垂直同期信号)が無いことだ。
line_valid と frame_valid があるので、カウンタを入れて、適当な所で同期信号を生成しようと思う。カメラのフレーム関連のパラメータは、”MT9D111のお勉強4”の Frame Time の図に書いてある。
MT9D111_6_121217.png

Video Input信号には、DEとData in、Vblank, Hblank, Vsync, Hsync, Video Clock がある。
DEはline_valid 信号が使える。Vblank はframe_valid を反転させれば良い。Hblankもline_valid を反転させる。問題はVsync とHsync で、これは適当に生成する必要がある。

最初に、水平同期信号を考える。Context Aを使用しているので、Horizontal Blanking Register は 0xAE = 174 pixels になっているはずだ。
フロント・ポーチを 64 クロック、HSYNCを 64 クロックとすると、パック・ポーチは 46 クロックとなるので、これで行ってみようと思う。
次に、垂直同期信号を考える。同様に、Vertical Blanking Register は 0x10 = 16 rows になっている。
フロント・ポーチを 6 rows、VSYNCを 4 rows、バック・ポーチを 6 rows にしてみようと思う。

AXI4-Stream のクロックは、Video In to AXI4-Stream IPの中で非同期FIFOでビデオ信号のクロックと切り離されているので、どのような動作周波数にしても問題がないが、100MHzくらいにしておこうと思う。

(2013/05/28:大幅修正)

実際にVerilog HDLファイルを修正していたところ、カメラのデータは1バイトずつ送られて来るのだが、それを2つ集めて1つのRGB 565のピクセルとしていることを思い出した。これでは、Video In to AXI4-Stream IPにPCLKでビデオデータとして転送するとこは不可能だということに気がついた。Video In to AXI4-Stream IPを使用するには、転送クロックを半分に落として、データ同期用の非同期FIFOを置いて、新たにビデオ信号を生成する必要がある。負担が大きいため、Video In to AXI4-Stream IPは使用しないで、そのフォーマットでカメラ・インターフェースIPから24ビットRGBを直接AXI4-Stream バスで出力することにした。

AXI4-Streamバスで出力する際に、TKEEPを2クロックごとにディアサートして、ストリーミングできるかな?と考えたが、TKEEPはストリームの最後でアライメントされていないデータのためにヌルバイトを許容するための信号のようだ。ましてやストリームの途中で全てヌルバイトにすることは許容されないはず。(”AXI リファレンスガイド UG761 (v13.1) 2011 年 3 月 7 日”53ページ、”パケットとヌルバイト”参照)

TKEEPの代わりに、TVALIDを2クロックごとにアサートしようと考えている。こうしても大丈夫らしい。”AXI リファレンスガイド UG761 (v13.1) 2011 年 3 月 7 日”の63ページ、図 4-3 : AXI4-Stream の波形を下に引用する。
Camera_Display_w_VDMA_5_130528.png

これで、カメラから出力されたPCLKでAXI4-Stream を駆動することができる。
  1. 2013年05月27日 04:56 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

カメラ、ビデオ表示カスタムIPを AX4-Stream に変更1(使用するIPの検討)

現在、AXI4 Master バスを使用して、PS部のDDR3 コントローラにAXI HPポート経由で画像データを書き込んでいたカメラ・インターフェイスIPを、AXI4-Streamバス対応に変更しようと思う。これは、AXI VDMAを使用して、画像データをDMAしようと思っているからだ。
AXI VDMAの使い方がとっても難しいので、AXI4-Stream対応カメラ・インターフェイスIPとAXI4-Stream対応ビットマップ・ディスプレイ・コントローラIPを作り、レジスタを設定できるシミュレーションAXI4 Master IPを付けてAXI VDMAの動作についてシミュレーションで、調べてみようと思っている。

まずは、性善説を信じて、現在入れているFIFOバッファを無くして、カメラから受けたRGB565を32ビットのデータに変換して、AXI4-Stream へ出力しようと思う。とりあえず、データをバッファするFIFOも無くすので、簡単な構成になるはずだ。

なお、AX4-Stream バスについては、”AXI4-Stream のお勉強”を参照下さい。

(2013/05/26追記)
なるべく既存のIPは使うという方針で行くことにしているので、XPSのVideo and Image Processing カテゴリのIPを見ると、AXI4-Stream to Video Out とVideo In to AXI4-Stream があった。この2つはVideo Timing Controller のビデオ信号とAXI4-Stream を変換するIPだった。

最初にVideo In to AXI4-Stream について書く。
LogiCORE IP Video In to AXI4-Stream v2.00.aProduct GuidePG043 July 25, 2012 (Video In to AXI4-Stream) を見るとブロック図が載っていた。Figure 1-1を下に転載する。
Camera_Display_w_VDMA_2_130526.png

Figure 1-1に示された様に、ビデオ信号をAXI4-Stream に変換するIPのようだ。カメラからの信号はこのビデオ信号に完全には適合しないが、適当な幅の HSYNC と VSYNC を作って入れれば行けるのではないか?と思う。カメラ・インターフェイスをVideo In to AXI4-Streamのビデオ信号に変換するカスタムIPを設定することにする。
(2013/05/28:修正)カメラ・インターフェースIPからVideo in to AXI4 Stream のフォーマットのAXI4-Streamバスで出力するカスタムIPを作ることにした。詳しくは、”カメラ、ビデオ表示カスタムIPを AX4-Stream に変更2(カメラ・インターフェースIP)”を参照のこと。

次に、LogiCORE IP AXI4-Stream to Video Out v1.0 Product Guide PG044 April 24, 2012 (AXI4-Stream to Video Out) を見ると、こちらにもブロック図がある。同様にFigure 1-1を下に転載する。
Camera_Display_w_VDMA_3_130526.png

Figure 1-1に示された様に、こちらはAXI4-Stream をビデオ信号に変換するIPのようだ。ビデオ信号はアナログRGBのVGA出力はそのまま駆動できるし、HDMIのADV7511はそれ用のフォーマットに変換すれば行けそうだと思う。こちらもそのようなカスタムIPを作成することにする。

ちなみに、ビデオ信号のフォーマットは、LogiCORE IP Video Timing Controller v3.0 DS857 June 22, 2011 Product Specification に載っていた。これまた、Figure 1 を転載させていただく。
Camera_Display_w_VDMA_4_130526.png

既存のIPを使用するは簡単だが、マニュアルを読むのが大変そうだ。

次の記事は、”カメラ、ビデオ表示カスタムIPを AX4-Stream に変更2(カメラ・インターフェースIP)
  1. 2013年05月26日 04:13 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

チャレンジ筑波に参加しました

今日は中学校から一路、徒歩で筑波山神社を目指し、そこから筑波山の登山道を登って、筑波山の御幸ヶ原へ、そこから女体山の頂上の直下を通って、ロープウェイのつつじケ丘駅に降りて中学校へ帰って来ました。歩いた距離は合計直線距離で20kmです。これが、中学校の行事のチャレンジ筑波です。本来は中学生だけなのですが、親も参加できるので、今年が最後の年なので参加して来ました。奥さんも一緒です。うちの奥さんは上の子供ときも含めて5年間参加しています。

中学校から午前7時30分ころに出発して、徒歩で最初の目的地の麓の筑波総合体育館へ向かいました。途中はとっても良い天気で暑かったです。筑波山がよく見えました。
tyaretsuku_1_130524.jpg

筑波総合体育館でトイレ休憩後につくば道を歩いて、筑波山神社まで登りました。筑波山神社に近づくに連れて登りになります。バテている中学生もいましたね。やっと筑波山神社に到着しました。これが午前11時くらいかな?
tyaretsuku_2_130524.jpg

約90分程度かかって、険しい山道を登って御幸ヶ原に付きました。今年の1月2日に登った反省を含めて、筑波山神社で杖を買っていったのと、体を鍛えていたので、少しは楽でした。特に杖があると、足がヘタってきてもう少し体を上げたいときに腕でアシストすることができます。午後1時ころ着きました。ここでお昼です。弁当はサンドイッチでしたが、上りで汗を書いているので、パサパサで喉を通らず、おにぎりに梅干しのほうが良かったです。それに汗をかいているので、塩辛いものが欲しくなります。下の写真は御幸ヶ原から見た景色です。
tyaretsuku_3_130524.jpg

tyaretsuku_4_130524.jpg

tyaretsuku_5_130524.jpg

その後で、女体山の頂上直下に歩いて行きました。ここはゆるい登りです。がま石がありました。
そこから下りですが、かなりきついです。鎖が張ってあるところもあります。ここにも、母の胎内くぐり、北斗岩、弁慶七戻りなどの岩がありました。下の写真はだいぶ下ってきたところです。ツツジが咲いていました。
tyaretsuku_6_130524.jpg

やっとつつじヶ丘に着きました。5時間30分、19.7kmの行程でした。これは直線距離のみです。そこからバスで中学校に帰って来ました。とっても疲れましたが良い思い出になりました。
  1. 2013年05月24日 20:20 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

CORDIC IP

XilinxのCoregenにCORDIC IPがある。CORDIC IP では、次の種類の方程式を実行できるそうだ。

  • 直交座標⇔極座標の変換
  • 三角関数
  • 双曲型
  • 平方根


CORDIC V5.0 のデータシート

「サルでも分かるCORDICアルゴリズム」によると、

CORDICとはCOordinate Rotation DIgital Computerの略で,関数の値を足し算,引き算のみで求めてしまう優れたアルゴリズムだ.


だそうだ。自分でアルゴリズムをIP化も出来そうだ。「サルでも分かるCORDICアルゴリズム」にアルゴリズムの説明が詳しく載っている。
  1. 2013年05月24日 04:23 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

ZedBoard用フルカラーDVI出力アダプタ

結構前になりますが、エム・アイ・エーにZedBoard用フルカラーDVI出力アダプタを注文して届きました。
エム・アイ・エーのZedBoard用フルカラーDVI出力アダプタ販売のページです。
FPGAマガジン No.1の46ページ、47ページに載っていたので、購入しました。
これを、ZedBoardの差動入出力のPMODに付けてみました。
FullColor_HDMI_Ad_1_130521.jpg

こんな感じです。でも、まだHDLソースがFPGAマガジンのサイトからダウンロードできるようになっていないので、HDMIコネクタからDVIで出力することができません。ただの飾りです。早くHDLソースがダウンロード出来るようになると良いと思います。よろしくお願いします。

あまり、HDLをダウンロードできるのが遅いようでしたら、AtlysボードのDVI出力用のHDLを修正して画像を出力できるようにしようか?と考えています。
  1. 2013年05月22日 05:26 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

AXI VDMAのお勉強4(AXI VDMA General Operations 1)

最初の記事は、”AXI VDMAのお勉強
前の記事は、”AXI VDMAのお勉強3(Scatter Gather Mode)

AXI VDMA General Operations(93ページ)
AXI VDMA Frame Boundary
・フレーム境界はモードによって様々。

・external fsync mode の場合は、チャネルの同期入力、mm2s_fsync と s2mm_fsync でフレーム境界が決まる。
・AXI VDMAが同期出力の mm2s_fsync_out か s2mm_fsync_out をアサートした後で、フレームの画像データ転送が始まる
・同期入力から同期出力までは、パイプラインの遅延があるそうだ。

・free run mode (C_USE_FSYNC = 0) では、内部で同期信号を生成している。
・フレーム境界で、同期出力の mm2s_fsync_out か s2mm_fsync_out をアサートする。

・S2MMチャネルの場合は、予定のデータを受信した時にハングアップするのを避けるため s_axis_s2mm_tready をアサートし続ける。これは、C_USE_FSYNC=1,3 and C_FLUSH_ON_FSYNC=1,3の場合だ。
・それは、適切なエラービットをセットするそうだ。エラービットのテーブルは、Table 3‐12: MM2S Errors、Table 3‐13: S2MM Errors(112ページ)を参照。

・画像のパラメータをアップデートしたときは、同期出力の mm2s_fsync_out か s2mm_fsync_outと一緒に、mm2s_prmtr_updateまたはs2mm_prmtr_updateをアサートする。

推奨:S2MMのデットロックのシナリオは、TUSER(0)をSOFとして使用していた時、ストリーミング・マスタが、s2mm_prmtr_updateがアサートするのを待って、s_axis_s2mm_tvalidをドライブする時だ。AXI VDMA は、TUSER(0)がアサートされ、s2mm_prmtr_updateをドライブするのを想定しているからだ。ストリーミング・マスタは、データ転送する際に s2mm_prmtr_update に依存しないようにする。


AXI VDMA Video Transfer
・データはシステムメモリからストリームまたは現在操作対象のフレームと垂直サイズ、水平サイズ、ストライドの開始アドレスで定義されているシステムメモリにストリームで転送される。
・下の図に、システムメモリに記憶された一般的なビデオ画像を示す。画像メモリの一部分を表示している。
・Horizontal Sizeのバイト数分のVertical Sizeのラインが、システムメモリのビデオ・フレームのスタート・アドレスの番地から転送される。
Study_of_VDMA_2_130517.png

Free Run Mode (C_USE_FSYNC = 0)
・free run mode (C_USE_FSYNC = 0)では、ビデオデータは可能な限り速く転送される。
・フレームの境界を示すのが、mm2s_fsync_outとs2mm_fsync_out。
・スタートアップ時に、各チャネルのDMACR.RSを1にし、ビデオのパラメータを設定した後で、最初の同期信号(mm2s_fsync_out か s2mm_fsync_out)をアサートする。(AXI VDMAが)
・最初の同期信号(mm2s_fsync_out か s2mm_fsync_out)をアサートすると最初のフレームが始まる。
・MM2Sチャネルでは、mm2s_fsync_outをアサート後に、ビデオデータがDMA転送される。
・S2MMチャネルでは、s2mm_fsync_outの後で、フレームのビデオデータをAXI VDMAにDMA転送する。

Frame Sync Mode (C_USE_FSYNC = 1,2,3)
・フレーム同期モードでは、MM2Sチャネルは mm2s_fsync、S2MMチャネルでは s2mm_fsync の外部同期信号にビデオデータが同期する。
・axi_vdmaチャネルは、関連するチャネルフレーム同期入力、mm2s_fsyncまたはs2mm_fsyncの立ち下がりエッジで各フレームを開始する。
・MM2Sチャネルの場合、データはmm2s_fsyncのアサート後にマスターAXI4-Streamのポートに出力される。外部のビデオ用IPは、次の mm2s_fsync まで、その出力されたデータをすべて受け取る必要がある。
・S2MMチャネルの場合、データはs2mm_fsyncのアサート後にスレーブAXI4-Streamのポートで受け取られる。
(注)FSYNCの異なるソースのためにS2MM_DMACR (S2MM DMA Control Register – Offset 30h) (C_INCLUDE_SG = 1/0)を参照のこと。

概要じゃなくて訳を書いてしまうとまずいので、Genlockなども要約したのだが、自重しようと思う。
  1. 2013年05月20日 05:14 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

クレヨンしんちゃん、B級グルメ、サバイバル(映画)を見てきた

今日は下の娘と、クレヨンしんちゃん、B級グルメ、サバイバル(映画)を見て来ました。とっても面白かったです。笑いました。クレヨンしんちゃんシリーズはとっても面白いです。好きです。
  1. 2013年05月19日 17:59 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

AXI VDMAのお勉強3(Scatter Gather Mode)

最初の記事は、”AXI VDMAのお勉強
前の記事は、”AXI VDMAのお勉強2(Register Direct Mode)

Scatter Gather Mode (C_INCLUDE_SG = 1) (92ページ)
Scatter Gather Modeとは、AXI VDMA操作が実行されるDMA操作のリストを保持するメモリ常駐のデータ構造を必要とする。DMA命令のリストは、ディスクリプタのチェーンと呼ばれるもので構成されている。各ディスクリプタは、処理すべき次の記述子へのポインタを持っていて、最後のディスクリプタはチェーンの最初のディスクリプタへのポインタを持っている。つまりチェーンをグルっと回ってDMA転送しているわけね。

VDMAを行う前に、descriptor pointer registers と DMA control registers をセットアップする。下に最低限の手順を示す。

1.チャネルの CURDESC_PNTRレジスタに有効なポインタを書く (Offset 0x08 for MM2S and 0x38 for S2MM)。

2.チャネルの DMACRレジスタにコントロール情報を書く (Offset 0x00 for MM2S and 0x30 for S2MM) 。いろいろな設定があるが、DMACR.RS=1でDMAが開始される。DMAのステータス・レジスタのHalt解除(DMASR.Halted = 0)にはタイムラグがあるそうだ。

3.チャネルの TAILDESC_PNTRレジスタに有効なポインタを書く (Offset 0x10 for MM2S and 0x40 for S2MM)。ディスクリプタをフェッチし、そして処理してチャネルをスタートする。

4.DMA scatter gather処理は、TAILDESC_PNTRが処理されるまで続く。終了すると DMASR.Idle = 1となる。

・C_NUM_FSTORESの値だけ、ディスクリプタのチェーンがある。
・Scatter Gatherエンジンは、ディスクリプタを読んで、内部レジスタをアップデートする。
・画像のタイミング情報 (vsize, hsize, stride, and frame delay)は、最初のディスクリプタから読まれて、その後のディスクリプタの画像情報は無視される。
・スタート・アドレスは、C_NUM_FSTORESの数のスタート・アドレス・レジスタに書き込まれる。

・ディスクリプタのポインタが、TAILDESCなった次の frame sync(C_USE_FSYNC = 0だと内部同期信号で、 C_USE_FSYNC = 1,2,3だと外部同期信号)でIDLE状態になるそうです。
・次の frame syncで、DMAコントローラは、新たに更新された値で動作するように内部レジスタセットを変更する。(あまり意味がよくわからない?)
・途中、言っている意味がよくわからないので飛ばします。
・DMAコントローラは、SGエンジンは、末尾ポインタに到達して一時停止した場合でも、映像データを転送し続ける。これは、中断のないビデオ·データ転送を可能にする。

Updating Video Transfer Information(93ページ)
・ビデオ転送情報を更新するには、CPUが新しいスタート・アドレス、vsize, hsize, stride や frame delay情報をTAILDESC で示されたメモリアドレスの後に書く。
・その後で新しいTAILDESC を指定すると、自動的に Scatter Gather DMAするようです。
・チャネルに新しいパラメータで動作した時、mm2s_fsync と s2mm_fsync に同期して、mm2s_prmtr_update とs2mm_prmtr_update が出力される。

(重要) On the S2MM channel, new video line size and number of video lines need to change following the assertion of s2mm_prmtr_update or undefined results occur.
そのまま引用。S2MMチャネルでは、画像のラインサイズやライン数を変更した後に、s2mm_prmtr_update がアサートされるか、定義されていない結果が起こると言っているのだろうか?(英語に自信が無いので、すみません)

・ AXI VDMA Scatter Gatherエンジンは、MM2SとS2MMチャンネルに独立のディスクリプタキューと内部レジスタセットを持っていて、独立に動作する。

Additional Design Information(101ページ)
Scatter Gather Descriptor
ディスクリプタ・チェーンは、MM2S_FRMSTORE and S2MM_FRMSTORE ディスクリプタから構成されて、1つのディスクリプタは32ビット7個で構成されるそうです。
ディスクリプタの構成が102ページから105ページまでに書いてあります。
  1. 2013年05月19日 05:15 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

AXI VDMAのお勉強2(Register Direct Mode)

最初の記事は、”AXI VDMAのお勉強
前の記事も、”AXI VDMAのお勉強

”LogiCORE IP AXI Video Direct Memory Access v5.04a Product Guide PG020 December 18, 2012”を参考にしながら、勉強したことを書いておこうと思う。
AXI VDMAはマニュアルが152ページもあって、どうやって使うか覚えるのが大変だが、90ページの Programming Sequence を読みながら、覚書を書いていこうと思う。

まず、レジスタについては、30ページからの Register Space を参照のこと。MM2S用、S2MM用のレジスタが並んでいる。印象的なのは、それぞれのスタート・アドレス・レジスタが16個ずつあることだ。

Register Direct Mode (C_INCLUDE_SG = 0) (90ページ)
・register direct modeは、ビデオのパラメータとスタート・アドレス・レジスタに開始アドレスをセットして、DMA Control Register をセットする。

1. DMACR register (Offset 0x00 for MM2S and 0x30 for S2MM)に値をセットする。下に、Figure 2‐3: MM2S DMACR Register を引用する。
Study_of_VDMA_1_130517.png

いろいろな設定があるが、DMACR.RS=1でDMAが開始される。DMAのステータス・レジスタのHalt解除(DMASR.Halted = 0)にはタイムラグがあるそうだ。
DMACR = 0x00000003 ( Circular Mode, Run)

2.ビデオのフレームバッファのアドレスを1からN個まで書く。Nはどこで決まっているかというと、C_NUM_FSTORES で決まるディフォルトは3個。0x5Cから0x98までの16個がMM2S用で、0xACから0xE8までの16個がS2MM用。

3.Frame DelayとストライドをFRMDLY_STRIDEレジスタに設定する (Offset 0x58 for MM2S and 0xA8 for S2MM)。
ビットマップ・ディスプレイ・コントローラで、1ピクセルRGB合計4バイトで表される。
ストライドは、SVGAの横ピクセル800ピクセル X 4バイト = 3,200を設定する。(94ページのAXI VDMA Video Transfer、Figure 3‐1: Example Video Image Transfer を参照)描画領域は1面しか用意しないため。図1参照。
Study_of_VDMA_2_130517.png
図1 画像データの描画領域

4.有効な水平サイズをHSIZEレジスタに設定する。 (Offset 0x54 for MM2S and 0xA4 for S2MM)

5.有効な垂直サイズをVSIZEレジスタに設定する。 (Offset 0x50 for MM2S and 0xA0 for S2MM)

画像データを転送するチャネルがスタートする。

(重要) On the S2MM channel, new video line size and number of video lines need to change following the assertion of s2mm_prmtr_update or undefined results occur.
そのまま引用。S2MMチャネルでは、画像のラインサイズやライン数を変更した後に、s2mm_prmtr_update がアサートされるか、定義されていない結果が起こると言っているのだろうか?(英語に自信が無いので、すみません)

Updating Video Transfer Information (91ページ)

Register Direct Mode (C_INCLUDE_SG = 0)で、描画している最中に、画像のパラメータやスタート・アドレスをいじることができる。垂直サイズ・レジスタに書くと、フレーム境界で反映される。
変更された画像パラメータをAXI VDMAが使うと、各チャネルの pmrtr_update 出力 ( mm2s_prmtr_update and s2mm_prmtr_update ) がアサートされる。
AXI VDMAが動作中に画像パラメータを変更するときは、初期化の時と同様に設定する。

1.変更したいチャネルに、任意の順序で Frame Delay, Stride, and Horizontal Size を書く。

2.最後に、Vertical Size を書く。Vertical Size をレジスタに書いた時には、すぐに動作中のパラメータに入れされずに、フレーム境界で変更される。(当たり前ですよね。画像が乱れちゃう。内部レジスタに書かれてから、フレーム境界でコピーされるようです)

C_ENABLE_VIDPRMTR_READS = 0 にすると、Register Direct Mode で画像パラメータ用のレジスタ (VSIZE, HSIZE, FRMDLY_STRIDE, and START ADDRESS/ES) をディスエーブルして、FPGAのリソースを削減できるそうです。最初に決めたパラメータから変更できないということでしょうかね?

次の記事は、”AXI VDMAのお勉強3(Scatter Gather Mode)
  1. 2013年05月17日 05:51 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

アルテラSoCシンポジウムに行って来ました

昨日、アルテラSoCシンポジウムに行って来ました。

Zynq同様にデュアルコア ARM® Cortex™-A9のHPS(アルテラではPSでなくHPS)とFPGAがチップに入っています。Cyclone V SoCのデュアルコア ARM® Cortex™-A9は800MHz動作といってました。スピードグレードで変更するかも?といってましたが、基本的には 800MHz なんですね?(ZedBoard の Zynq の ARMコアの動作周波数は 667MHz ですが、スピードグレードによって違います)
Zynqとの大きな違いは、Zynq で言うとAXI ACPポートは最大64ビット幅なのですが、SoC FPGAだと最大128ビット幅だそうです。スループットが同じ動作周波数だったら2倍になりそうです。AXI HPポートに類するポートはなかったですが、HPSのDDRコントローラにアクセスするAXIポートが6個あるそうです。ツールでそのポートを設定する時にプルダウンメニューを見ていましたが、256ビット幅までありました。そんなにビット幅があるんでしょうか?かなりスループットがでるのかも?です。そうそう、この辺のAXIポートを設定するツールのデモを見ていたらAXI3と書いてあった気が?します。Xilinxと同じですね。この辺りはARMのIPコアの制限なんでしょうかね?
FPGA側にDDRコントローラのハードコアを載せているのも、大きな違いです。これがあると、FPGA側で画像のフレームバッファを組むことができます。帯域も計算しやすいですね。(2013/09/13:追記)ZynqでもソフトコアでDDRコントローラをPL側に実装できますね。そういうボードがありました。(OZ745 - Zynq™ SoC Video Development Kit)参考文献”7 シ リーズ FPGAメモリ インターフェイス ソリューション”。
後は、FPGAだけでもROMからコンフィギュレーションできるそうです。

Qsysも使いやすそうでしたが、やはり接続が1次元です。見やすさは、2次元で回路図のようにIPを接続できるXilinxのVivado IP Integrator の方が見やすそうでした。使ったことがなくてビデオで見ただけですし、あくまで見やすいという観点だけです。(参考、動画です。Targeting Zynq Using Vivado IP Integrator
そうそうARM Development Studio 5 (DS-5) Altera Edition ツールキットとSignalTap II が協調して検証できるのは素晴らしいですね。こういう検証環境を使ってみたいです。Web EditonではDS-5のリモートデバックのみで、USBブラスタは使えないとのことでした。実質、Linuxのみデバックできるということでしょうかね?

評価ボードですが、4種類紹介がありました。
1.Cyclone V SoC 開発キット
値段は、$1,595なので高価ですが、DS-5がUSBブラスタで使えるライセンスが付いているそうです。このボードはHPS部とFPGA部にDDR3が付いているそうです。

2.Helio(ヘリオ) ボード ~Cyclone V SoC ベーシックボード~
25,800円(税別)だそうです。このボードは、FPGA部にDDR3は載っていませんが欲しいです。1.のアルテラ純正ボードとBSP(Board Support Package)互換だそうです。同じSDカードでLinux起動していました。

3.Arrow's SoCKIT
値段はわかりませんが、スイッチなどが付いていて、DE0の雰囲気のボードです。このボードもHPS部とFPGA部にDDR3が付いているそうです。

4.EBV SoCrates Evaluation Board
円盤状の変わったボードです。値段はわかりません?

最後に、SoC FPGAのコニュニティサイトが立ち上がっているそうです。RocketBoards.orgです。ボードの情報もあります。

追加です。現在のAltera の OpenCLは、プロセッサがx86(古いですか?Intel のPC用のプロセッサ)対応だそうですが、SoC FPGA用のOpenCLも対応中だそうです。

もう一度追加。富士ソフトSoC FPGA にAndroid をポーティングしていました。記憶に間違いがなければ、オープンソースとして出す?そうです(間違っていたらごめんなさい)。グラフィックエンジン(1部かな?)はハードウェアにオフロードしてあるそうです。GPUじゃないとのことです。Android に必要な機能だけを抜き出してFPGAに実装してあるとのことでした。このIPも評価版としてバイナリだけども出すそうです。バイナリと言うのは、SOFファイルなんでしょうか?バイナリなので、ネットリストではないと思います。デモを見て来ましたが、当然ながら描画機能をFPGAにオフロードしてあるほうが速かったです。

#セミナの資料は、全くもらっていないので、記憶に頼っているため不正確な場合があります。ご了承下さい。また、間違っていたら、コメントでご指摘下さい。よろしくお願い致します。
  1. 2013年05月16日 05:34 |
  2. その他のFPGAの話題
  3. | トラックバック:0
  4. | コメント:0

AXI VDMAを使ったカメラ表示回路の作製1(構想)

自作IPでのトリプルバッファリングが完成した
今度は、トリプルバッファリングを行うことができるAXI VDMAを使って、現在、ZedBoardでカメラ画像を表示ているシステムを構成してみようと思う。
今までの、AXI VDMAのことを書いたのブログ記事は、”AXI VDMAのお勉強”がある。

AXI VDMAは、CMOSカメラからの画像をAXI4-Stream で受けて、AXI3 Master WriteでDDR3 SDRAMへWriteしながら、DDR3 SDRAMにバッファされたCMOSカメラからの画像データをAXI3 Master Read で読んできて、AXI4-Stream でビットマップ・ディスプレイ・コントローラへ画像データを転送する。下にそのブロック図を示す。
Camera_Display_w_VDMA_1_130514.png

これから、カメラ・インターフェイスIPとビットマップ・ディスプレイ・コントローラIPをAXI4-Streamに対応するように修正していく予定だ。

AXI4-Stream の解説をしたブログ記事は、”AXI4-Stream のお勉強”がある。
  1. 2013年05月14日 05:35 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

Triple Frame Buffer Controller の追加5(インプリメント、実機検証)

Triple Frame Buffer Controller の追加4(IPの追加)”で、トリプルバッファリングのIPをXPSプロジェクトに追加した。
今回は、インプリメントを行なって実機で試してみた。

・PlanAheadでのインプリメントは無事に終了した。
Triple_FBC_14_130511.png

・その後、SDKにハードウェアをエクスポートして、リコンパイルを行った。

・ZedBoardにビットストリームをダウンロードして、ソフトウェアをRunさせた。
Triple_FBC_15_130512.png

カメラの画像は表示されなかった。バグだ。
Triple_FBC_18_130513.jpg

ChipScope を入れて、いろいろとやってみたが、原因はわからなかった。camera_fb_start_addrも、bitmapd_fb_start_addrも正常のようだ。
原因を突き止めたのは、カメラ・インターフェイスIPとビットマップ・ディスプレイ・コントローラIPのアドレス処理回路にChipScope のプローブを付けた時だった。下に示す。
Triple_FBC_16_130512.png

上の図で、addr_count がビットマップ・ディスプレイ・コントローラIPのアドレス、paddr_reg がカメラ・インターフェイスIPのアドレスを示している。paddr_reg は、正しいアドレスだが、addr_count はアドレスが若すぎる。(addr_count のビットは、[31:3]なので、左に3ビットシフトする。つまり、1F040 は F8200 )

これはおかしいということで調査したところ、ビットマップ・ディスプレイ・コントローラIPの下位モジュールで、フレームバッファのアドレスを渡すのを忘れていた。下位モジュールのインスタンスの所で、フレームバッファアドレスを下位モジュールに接続する記述を忘れていた。
Triple_FBC_20_130513.png

これで、PlanAheadでインプリメント時に、エラーもワーニングも出ていなかったのだが、これは困る。エラーかワーニングを出してほしい。

ともかく、この行を追加して、もう一度、インプリメントを行った。今度は、ビットマップ・ディスプレイ・コントローラIPのアドレスも正常になった。
Triple_FBC_17_130512.png

見えているビットマップ・ディスプレイ・コントローラIPのアドレスは、0344B240 なので、左に3ビットシフトしたら、1A259200 だった。

ディスプレイに表示してみると、正常に表示された。手を画面上で動かしても横筋は見えない。
Triple_FBC_19_130513.jpg

SDKでBOOT.bin を作製してSDカードに書いたら正常に動作した。PS-RSTボタンを押さなくても画面が正常に表示されている。どうやら、以前のバージョンで作成したビットストリームをISE14.5のSDKでRun させたり、ブート用のvbinファイルを作成すると調子が悪いようだ。


最後に、カメラのクロックをオシロスコープで測定してみた。カメラに与えているクロックは、PS部のクロックを使用していて、35.714283MHzの設定だった。
実際にカメラに与えているXCLKを下に示す。周波数の実測値は 35.715MHz だった。
Triple_FBC_21_130513.jpg

カメラから出力されるPCLK は、17.857MHz だった。入力の約半分の周波数だ。これだとフレームレートは 15fps になる。
Triple_FBC_22_130513.jpg
  1. 2013年05月13日 05:08 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

さくらんぼ収穫、さくらんぼジャム

上の娘の小学校卒業記念の記念樹のさくらんぼの木は毎年すごい数のさくらんぼの実を付けます。昨日と今日とさくらんぼを収穫しました。
下の写真のように、伸び縮みする脚立を買ってあるので、それを使って収穫しています。
cherry_1_130512.jpg

さくらんぼが沢山実っているのが見えますか?ズームしてみます。
cherry_2_130512.jpg

更に、ズームします。
cherry_3_130512.jpg

売っているさくらんぼに比べて小さいですが、沢山実っています。食べてみると、素朴な味です。少し味は薄めですが、甘酸っぱい感じです。
さくらんぼ収穫の成果です。かなり収穫出来ました。
cherry_4_130512.jpg

昨日の収穫したさくらんぼで作ったジャムです。もっと沢山できています。今朝のヨーグルトに入れて食べたら美味しかったです。でも、ジャムの下ごしらえとして、1つ1つ種を出すのが大変です。
cherry_5_130512.jpg

今日の収穫もジャムになりました。まだ、さくらんぼは沢山残っていますが、ジャムにするのが大変だし、来週までには鳥さんに食べられちゃうと思います。

本当に緑が萌え出す季節になりました。やっと花粉の季節も終わって楽になりました。下の写真は左の木が柿の木で、右が梅の木です。梅の木には梅の実がなっています。収穫は6月くらいですかね?柿の木も次郎柿が3本、富有柿が1本あります。秋が楽しみです。
cherry_6_130512.jpg
  1. 2013年05月12日 12:05 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

Triple Frame Buffer Controller の追加4(IPの追加)

前回、”Triple Frame Buffer Controller の追加3(単体シミュレーション)”で、Triple Frame Buffer Controller の単体シミュレーションが成功した。今回は、XPSプロジェクトに、Triple Frame Buffer Controller IP を追加する。

・XPSプロジェクトでRescan User Repositories を実行して、Triple Frame Buffer Controller IP をXPSに認識させた。

・XPSプロジェクトのUSER に triple_fb_cntler が追加された。triple_fb_cntler の右クリックメニューから Add IP を選択した。
Triple_FBC_8_130511.png

・ダイアログが出るので、Yes ボタンをクリックした。
Triple_FBC_9_130511.png

・ triple_fb_cntler_0 の設定ダイアログが出た。そのまま OK ボタンをクリックした。
Triple_FBC_10_130511.png

・triple_fb_cntler_0 がXPSプロジェクトに入った。
Triple_FBC_11_130511.png

・Ports タブで、triple_fb_cntler_0 のポートを下図の様に接続した。
Triple_FBC_12_130511.png

・カメラ・インターフェースIPとビットマップ・ディスプレイ・コントローラIPの対応するポートに接続した。
Triple_FBC_13_130511.png

・Project メニューから Desgin Rule Check を行ったらエラーはなかった。

これでXPSプロジェクトは完成したので、Hardware メニューから Clean Netlist を選択して、既存のネットリストを削除した。

最後にカスタムIP用のファイルを下に示す。
最初に、triple_fb_cntrler_v2_1_0.mpd を下に示す。

###################################################################
##
## Name : triple_fb_cntrler
## Desc : Microprocessor Peripheral Description
## : Automatically generated by PsfUtility
##
###################################################################

BEGIN triple_fb_cntrler

## Peripheral Options
OPTION IPTYPE = PERIPHERAL
OPTION IMP_NETLIST = TRUE
OPTION STYLE = HDL
OPTION DESC = triple_fb_cntrler
OPTION LONG_DESC = Triple Frame Buffer Controller
OPTION HDL = MIXED
OPTION RUN_NGCBUILD = FALSE

## Bus Interfaces

## Generics for VHDL or Parameters for Verilog
PARAMETER C_M_AXI_ADDR_WIDTH = 32, DT = integer, ASSIGNMENT = CONSTANT
PARAMETER C_DISPLAY_START_ADDRESS = 0x1A000000, DT = std_logic_vector(31 downto 0)

## Ports
PORT aclk = "", DIR = I, SIGIS = CLK
PORT aresetn = "", DIR = I, SIGIS = RST
PORT camera_fb_start_addr = "", DIR = O, VEC = [(C_M_AXI_ADDR_WIDTH-1):0]
PORT bitmapd_fb_start_addr = "", DIR = O, VEC = [(C_M_AXI_ADDR_WIDTH-1):0]
PORT frame_valid_1d = "", DIR = I
PORT display_enable = "", DIR = I
END


次に、triple_fb_cntrler_v2_1_0.muiを下に示す。

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE doc SYSTEM "../../ipdialog.dtd" [
    <!-- -->
    <!ENTITY C_M_AXI_ADDR_WIDTH '
    <widget id="C_M_AXI_ADDR_WIDTH">
        <key>C_M_AXI_ADDR_WIDTH</key>
        <label>C_M_AXI_ADDR_WIDTH</label>
        <tip></tip>
    </widget>
    '>
    <!ENTITY C_DISPLAY_START_ADDRESS '
    <widget id="C_DISPLAY_START_ADDRESS">
        <key>C_DISPLAY_START_ADDRESS</key>
        <label>C_DISPLAY_START_ADDRESS</label>
        <tip></tip>
    </widget>
    '>
]>

<doc>
    <view id="User">
        <display>User</display>
        <group id="Common">
            <display>Common</display>
            <item>&C_M_AXI_ADDR_WIDTH;</item>
            <item>&C_DISPLAY_START_ADDRESS;</item>
        </group>
    </view>
</doc>


最後に、triple_fb_cntrler_v2_1_0.pao を下に示す。

lib triple_fb_cntrler_v1_00_a triple_fb_state.v verilog
lib triple_fb_cntrler_v1_00_a triple_fb_cntrler.v verilog


これで、AXIバスやPLBバスを持たないで、他のカスタムIPに接続するだけのIPも作ることが出来た。
  1. 2013年05月11日 04:56 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

Triple Frame Buffer Controller の追加3(単体シミュレーション)

前回、”Triple Frame Buffer Controller の追加2(ポートの追加)”では、カメラ・インターフェイスIPとビットマップ・ディスプレイ・コントローラIPを書き換えてポートを追加した。
今回は、HDLファイルを書いて、単体でシミュレーションを行った。書いたVerilog HDLファイルは、triple_fb_state.v、triple_fb_cntrler.v、それにテストベンチの triple_tb_cntrler_tb.v だ。

早速、シミュレーション波形を見ていこう。下に示すのが、Writeつまりカメラのフレームレートが 15fps で、Readつまり、ディスプレイのフレームレートが 60fps の場合だ。
Triple_FBC_6_130510.png

camera_fb_start_addr と bitmapd_fb_start_addr を見ると、フレームバッファ1、フレームバッファ2、フレームバッファ3のアドレスを順番に表示していて、しかも2つのアドレスが競合しないのが分かる。

順番が逆になったが、Project Navigator の画面を下に示す。
Triple_FBC_7_130510.png

元の triple_tb_cntrler IP の階層では深過ぎて、ISimがエラーを出力してしまうので、階層を浅くしてシミュレーションを行なっている。(”カメラ・インターフェースIPにAXI4 Lite Slave インターフェースを追加1(ISimでエラー)”を参照)

これで単体シミュレーションは終了した。
下に、各 Verilog HDL ファイルを示す。

最初に、triple_fb_state.v

// Triple Frame Buffer State
// triple_fb_state.v
// 2013/05/09
//

`default_nettype none

module triple_fb_state (
    input    wire    clk,
    input    wire    reset,
    input    wire    write_req_p,
    input    wire    display_req_p,
    input    wire    frame_valid_p,
    input    wire    display_enable_p,
    output    reg        [1:0]    state
);
    localparam     wait_write =     2'b00,
                writing    =        2'b01,
                displaying =    2'b10,
                wait_display =    2'b11;
    
    always @(posedge clk) begin
        if (reset)
            state <= wait_write;
        else begin
            case (state)
                wait_write :
                    if (write_req_p)
                        state <= writing;
                writing :
                    if (write_req_p)
                        state <= writing;
                    else if (frame_valid_p)
                        state <= wait_display;
                wait_display :
                    if (display_req_p)
                        state <= displaying;
                displaying :
                    if (display_req_p)
                        state <= displaying;
                    else if (display_enable_p)
                        state <=wait_write;
            endcase
        end
    end
    
    // synthesis translate_off
     reg [12*8:1] FB_STATE;
     
     always @(state) begin
        case (state)
            wait_write:        FB_STATE <= "WAIT_WRITE";
            writing:        FB_STATE <= "WRITING";
            displaying :    FB_STATE <= "DISPLAYING";
            wait_display :    FB_STATE <= "WAIT_DISPLAY";
        endcase
    end    
    // synthesis translate_on
endmodule

`default_nettype wire    


次に、triple_fb_cntrler.v

// Triple Frame Buffer Controller
// triple_fb_cntrler.v
// 2013/05/09
//

`default_nettype none

module triple_fb_cntrler #(
    parameter integer C_M_AXI_ADDR_WIDTH            = 32,
    parameter [31:0]    C_DISPLAY_START_ADDRESS        = 32'h1A000000    // フレームバッファのスタートアドレス
)(
    input    wire    aclk,
    input    wire    aresetn,
    output    reg    [C_M_AXI_ADDR_WIDTH-1 :0]    camera_fb_start_addr,
    output    reg    [C_M_AXI_ADDR_WIDTH-1 :0]    bitmapd_fb_start_addr,
    input    wire    frame_valid_1d,
    input    wire    display_enable
);
    `include "./disp_timing_parameters.vh"

    localparam    wait_write =    2'b00,
                writing    =    2'b01,
                displaying =    2'b10,
                wait_display =    2'b11;
    localparam    FIRST_FB_ADDRESS =    C_DISPLAY_START_ADDRESS;
    localparam    SECOND_FB_ADDRESS =    C_DISPLAY_START_ADDRESS + (H_ACTIVE_VIDEO * V_ACTIVE_VIDEO)*4;
    localparam    THIRD_FB_ADDRESS =    SECOND_FB_ADDRESS + (H_ACTIVE_VIDEO * V_ACTIVE_VIDEO)*4;
    
    reg        fv_1d_1d, fv_1d_2d, fv_1d_3d;
    reg        de_1d, de_2d, de_3d;
    wire    reset;
    reg        fv_1d_p, de_p;
    reg        reset_1b;
    reg        write_req_p_1, write_req_p_2, write_req_p_3;
    reg        display_req_p_1, display_req_p_2, display_req_p_3;
    wire    [1:0] state_1, state_2, state_3;
    
    assign reset = ~aresetn;
    // Sychronization
    always @(posedge aclk) begin
        if (reset) begin
            fv_1d_1d <= 1'b0;
            fv_1d_2d <= 1'b0;
            fv_1d_3d <= 1'b0;
            de_1d <= 1'b0;
            de_2d <= 1'b0;
            de_3d <= 1'b0;
        end else begin
            fv_1d_1d <= frame_valid_1d;
            fv_1d_2d <= fv_1d_1d;
            fv_1d_3d <= fv_1d_2d;
            de_1d <= display_enable;
            de_2d <= de_1d;
            de_3d <= de_2d;
        end
    end
    
    always @(posedge aclk) begin
        if (reset)
            fv_1d_p <= 1'b0;
        else begin
            if (fv_1d_2d==1'b0 && fv_1d_3d==1'b1) // falling edge
                fv_1d_p <= 1'b1;
            else
                fv_1d_p <= 1'b0;
        end
    end
    
    always @(posedge aclk) begin
        if (reset)
            de_p <= 1'b0;
        else begin
            if (de_2d==1'b0 && de_3d==1'b1) // falling edge
                de_p <= 1'b1;
            else
                de_p <= 1'b0;
        end
    end
    
    always @(posedge aclk) begin
        reset_1b <= reset;
    end
    
    // write_req_p_1, write_req_p_2, write_req_p_3
    always @(posedge aclk) begin
        if (reset) begin
            write_req_p_1 <= 1'b0; write_req_p_2 <= 1'b0; write_req_p_3 <= 1'b0;
            camera_fb_start_addr <= FIRST_FB_ADDRESS;
        end else if (~reset & reset_1b) begin // reset falling edge
            write_req_p_1 <= 1'b1; write_req_p_2 <= 1'b0; write_req_p_3 <= 1'b0;
        end else if (fv_1d_2d==1'b0 && fv_1d_3d==1'b1) begin // falling edge
            if (state_1 == wait_write) begin
                write_req_p_1 <= 1'b1; write_req_p_2 <= 1'b0; write_req_p_3 <= 1'b0;
                camera_fb_start_addr <= FIRST_FB_ADDRESS;
            end else if (state_2 == wait_write) begin
                write_req_p_1 <= 1'b0; write_req_p_2 <= 1'b1; write_req_p_3 <= 1'b0;
                camera_fb_start_addr <= SECOND_FB_ADDRESS;
            end else if (state_3 == wait_write) begin
                write_req_p_1 <= 1'b0; write_req_p_2 <= 1'b0; write_req_p_3 <= 1'b1;
                camera_fb_start_addr <= THIRD_FB_ADDRESS;
            end else if (state_1==writing && state_2!=wait_write && state_3!=wait_write) begin
                write_req_p_1 <= 1'b1; write_req_p_2 <= 1'b0; write_req_p_3 <= 1'b0;
                camera_fb_start_addr <= FIRST_FB_ADDRESS;
            end else if (state_2==writing && state_1!=wait_write && state_3!=wait_write) begin
                write_req_p_1 <= 1'b0; write_req_p_2 <= 1'b1; write_req_p_3 <= 1'b0;
                camera_fb_start_addr <= SECOND_FB_ADDRESS;
            end else if (state_3==writing && state_2!=wait_write && state_3!=wait_write) begin
                write_req_p_1 <= 1'b0; write_req_p_2 <= 1'b0; write_req_p_3 <= 1'b1;
                camera_fb_start_addr <= THIRD_FB_ADDRESS;
            end else begin
                write_req_p_1 <= 1'b0; write_req_p_2 <= 1'b0; write_req_p_3 <= 1'b0;
            end
        end else begin
            write_req_p_1 <= 1'b0; write_req_p_2 <= 1'b0; write_req_p_3 <= 1'b0;
        end
    end
    
    // display_req_p_1, display_req_p_2, display_req_p_3
    always @(posedge aclk) begin
        if (reset) begin
            display_req_p_1 <= 1'b0; display_req_p_2 <= 1'b0; display_req_p_3 <= 1'b0;
            bitmapd_fb_start_addr <= FIRST_FB_ADDRESS;
        end else if (de_2d==1'b0 && de_3d==1'b1) begin // falling edge
            if (state_1 == wait_display) begin
                display_req_p_1 <= 1'b1; display_req_p_2 <= 1'b0; display_req_p_3 <= 1'b0;
                bitmapd_fb_start_addr <= FIRST_FB_ADDRESS;
            end else if (state_2 == wait_display) begin
                display_req_p_1 <= 1'b0; display_req_p_2 <= 1'b1; display_req_p_3 <= 1'b0;
                bitmapd_fb_start_addr <= SECOND_FB_ADDRESS;
            end else if (state_3 == wait_display) begin
                display_req_p_1 <= 1'b0; display_req_p_2 <= 1'b0; display_req_p_3 <= 1'b1;
                bitmapd_fb_start_addr <= THIRD_FB_ADDRESS;
            end else if (state_1==displaying && state_2!=wait_display && state_3!=wait_display) begin
                display_req_p_1 <= 1'b1; display_req_p_2 <= 1'b0; display_req_p_3 <= 1'b0;
                bitmapd_fb_start_addr <= FIRST_FB_ADDRESS;
            end else if (state_2==displaying && state_1!=wait_display && state_3!=wait_display) begin
                display_req_p_1 <= 1'b0; display_req_p_2 <= 1'b1; display_req_p_3 <= 1'b0;
                bitmapd_fb_start_addr <= SECOND_FB_ADDRESS;
            end else if (state_3==displaying && state_1!=wait_display && state_2!=wait_display) begin
                display_req_p_1 <= 1'b0; display_req_p_2 <= 1'b0; display_req_p_3 <= 1'b1;
                bitmapd_fb_start_addr <= THIRD_FB_ADDRESS;
            end else begin
                display_req_p_1 <= 1'b0; display_req_p_2 <= 1'b0; display_req_p_3 <= 1'b0;
            end
        end else begin
            display_req_p_1 <= 1'b0; display_req_p_2 <= 1'b0; display_req_p_3 <= 1'b0;
        end
    end
    
    // First Frame Buffer State
    triple_fb_state first_fb_state (
        .clk(aclk),
        .reset(reset),
        .write_req_p(write_req_p_1),
        .display_req_p(display_req_p_1),
        .frame_valid_p(fv_1d_p),
        .display_enable_p(de_p),
        .state(state_1)
    );
    
    // Second Frame Buffer State
    triple_fb_state second_fb_state (
        .clk(aclk),
        .reset(reset),
        .write_req_p(write_req_p_2),
        .display_req_p(display_req_p_2),
        .frame_valid_p(fv_1d_p),
        .display_enable_p(de_p),
        .state(state_2)
    );

    // Third Frame Buffer State
    triple_fb_state third_fb_state (
        .clk(aclk),
        .reset(reset),
        .write_req_p(write_req_p_3),
        .display_req_p(display_req_p_3),
        .frame_valid_p(fv_1d_p),
        .display_enable_p(de_p),
        .state(state_3)
    );

    // synthesis translate_off
    localparam    integer    CHAR_NUM    = 12;
    
    reg [CHAR_NUM*8:1] FB_STATE_1;
    reg [CHAR_NUM*8:1] FB_STATE_2;
    reg [CHAR_NUM*8:1] FB_STATE_3;

    always @(state_1) begin
        case (state_1)
            wait_write:        FB_STATE_1 <= "WAIT_WRITE";
            writing:        FB_STATE_1 <= "WRITING";
            displaying :    FB_STATE_1 <= "DISPLAYING";
            wait_display :    FB_STATE_1 <= "WAIT_DISPLAY";
        endcase
    end

    always @(state_2) begin
        case (state_2)
            wait_write:        FB_STATE_2 <= "WAIT_WRITE";
            writing:        FB_STATE_2 <= "WRITING";
            displaying :    FB_STATE_2 <= "DISPLAYING";
            wait_display :    FB_STATE_2 <= "WAIT_DISPLAY";
        endcase
    end
    
    always @(state_3) begin
        case (state_3)
            wait_write:        FB_STATE_3 <= "WAIT_WRITE";
            writing:        FB_STATE_3 <= "WRITING";
            displaying :    FB_STATE_3 <= "DISPLAYING";
            wait_display :    FB_STATE_3 <= "WAIT_DISPLAY";
        endcase
    end    
    // synthesis translate_on    
endmodule

`default_nettype wire


最後にテストベンチの triple_tb_cntrler_tb.v

// Triple Frame Buffer Controller Testbench
// triple_tb_cntrler_tb.v
//

`default_nettype none

`timescale 1us / 100ps

module triple_tb_cntrler_tb;
    parameter    integer    write_life_cycle    = 100000/15;    // 15fps
    parameter    integer display_life_cycle    = 100000/60;    // 60fps
    
    parameter integer C_M_AXI_ADDR_WIDTH            = 32;
    
    wire    clk;
    wire    reset;
    reg        [23:0] wtime;
    reg        [23:0] dtime;
    wire    [C_M_AXI_ADDR_WIDTH-1 :0]    camera_fb_start_addr;
    wire    [C_M_AXI_ADDR_WIDTH-1 :0]    bitmapd_fb_start_addr;
    wire    frame_valid_1d;
    wire    display_enable;
    
    // clk のインスタンス
    clk_gen #(
        .CLK_PERIOD(10),    // 10usec, 100kHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) clki (
        .clk_out(clk)
    );
    
    // reset_gen のインスタンス
    reset_gen #(
        .RESET_STATE(1'b1),
        .RESET_TIME(50)    // 50usec
    ) RESETi (
        .reset_out(reset)
    );

    triple_fb_cntrler #(
        .C_M_AXI_ADDR_WIDTH(C_M_AXI_ADDR_WIDTH),
        .C_DISPLAY_START_ADDRESS(32'h1A000000)
    ) triple_fb_cntrler_i (
        .aclk(clk),
        .aresetn(~reset),
        .camera_fb_start_addr(camera_fb_start_addr),
        .bitmapd_fb_start_addr(bitmapd_fb_start_addr),
        .frame_valid_1d(frame_valid_1d),
        .display_enable(display_enable)
    );
    
    always @(posedge clk) begin
        if (reset)
            wtime <= write_life_cycle - 1;
        else begin
            if (wtime == 24'd0)
                wtime <= write_life_cycle - 1;
            else
                wtime <= wtime - 24'd1;
        end
    end
    assign frame_valid_1d = (wtime > 24'd100) ? 1'b1 : 1'b0;
    
    always @(posedge clk) begin
        if (reset)
            dtime <= display_life_cycle - 1;
        else begin
            if (dtime == 24'd0)
                dtime <= display_life_cycle - 1;
            else
                dtime <= dtime - 24'd1;
        end
    end
    assign display_enable = (dtime > 24'd100) ? 1'b1 : 1'b0;
endmodule
    
module clk_gen #(
    parameter         CLK_PERIOD = 100,
    parameter real    CLK_DUTY_CYCLE = 0.5,
    parameter        CLK_OFFSET = 0,
    parameter        START_STATE    = 1'b0 )
(
    output    reg        clk_out
);
    begin
        initial begin
            #CLK_OFFSET;
            forever
            begin
                clk_out = START_STATE;
                #(CLK_PERIOD-(CLK_PERIOD*CLK_DUTY_CYCLE)) clk_out = ~START_STATE;
                #(CLK_PERIOD*CLK_DUTY_CYCLE);
            end
        end
    end
endmodule

module reset_gen #(
    parameter    RESET_STATE = 1'b1,
    parameter    RESET_TIME = 100 )
(
    output    reg        reset_out
);
    begin
        initial begin
            reset_out = RESET_STATE;
            #RESET_TIME;
            reset_out = ~RESET_STATE;
        end
    end
endmodule

`default_nettype wire


  1. 2013年05月10日 05:34 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

Triple Frame Buffer Controller の追加2(ポートの追加)

Triple Frame Buffer Controller の追加1(仕様の検討)”でトリプルバッファリングの仕様を検討した。今回は、カメラ・インターフェイスIPとビットマップ・ディスプレイ・コントローラIPの前回の図に示したポートを追加する。

最初に、2つのIPの C_DISPLAY_START_ADDRESS パラメータを削除して、ポートを追加した。下に、カメラ・インターフェイスIPのトップHDLファイル (mt9d111_inf_axi_master.vhd) の追加したポート部分を示す。

    -- for Triple Frame Buffer Controller
    fb_start_address    : in    std_logic_vector(C_M_AXI_ADDR_WIDTH-1 downto 0);
    frame_valid_1d        : out    std_logic
);


次に、ビットマップ・ディスプレイ・コントローラIPのトップHDLファイル (bitmap_disp_cntrler_axi_master.v) の追加したポート部分を示す。

        input    wire    [C_M_AXI_ADDR_WIDTH-1:0]    fb_start_address,
        output    wire    display_enable
    );


ともに、C_M_AXI_ADDR_WIDTH = 32だ。

PlanAheadプロジェクトを立ちあげた。
Triple_FBC_2_130508.png

ブログを書いている都合上、すでにXPSプロジェクトを修正しているので、Synthesis and Implementation Out-of-Date が表示されている。
次に、PlanAhead のProject Manager 上のSources ウインドウの system_i をダブルクリックして、起動したところエラーになってしまった。これは、カメラ・インターフェイスIPとビットマップ・ディスプレイ・コントローラIPのMPDファイルの PARAMETER の C_DISPLAY_START_ADDRESS を削除してしまったので、system.mhs の記述と合わなくなってしまったためと思われた。(system.mhsのある場所は、ZedBoard_BitMap_DispCont_wHDMI_145\ZedBoard_BitMap_DispCont_142.srcs\sources_1\edk\system)

そこで、system.mhs をエディタで開いて PARAMETER の C_DISPLAY_START_ADDRESS を削除した。system.mhs から PARAMETER の C_DISPLAY_START_ADDRESS を削除する様子を下の図に示す。
Triple_FBC_3_130508.png

上の図のピンクの枠で囲まれた C_DISPLAY_START_ADDRESS を削除したところ、XPSが正常に立ち上がった。
Triple_FBC_4_130508.png

カメラ・インターフェイスIPとビットマップ・ディスプレイ・コントローラIPのMPDファイルに追加するポートを記述して、XPSのProject メニューからReascan Usre Repositories を選択して、カスタムIPの再スキャンを行った。その結果、2つのIPにポートを追加できた。
Triple_FBC_5_130508.png

次は、Triple Frame Buffer Controller カスタムIPを作っていくことにする。
  1. 2013年05月08日 05:37 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

Triple Frame Buffer Controller の追加1(仕様の検討)

画像のフレームバッファ”と”画像のフレームバッファ2(ライフサイクルをシミュレーション)”で分かったことを、実際のZedBoard カメラ回路で確かめてみることにした。使用するのは、カメラ表示回路のベアメタル・アプリケーション版の”ZedBoard用CMOSカメラ回路の作製14(カメラボード改2)”の回路に変更を施すことにした。それで、物体を動かした時の横筋が見えなくなれば、めでたし、めでたしとなる。

変更は、Triple Frame Buffer ControllerをIPとしてXPSに追加することにした。XilinxのIPのAXI VDMAと違って、自作のカスタムIPは、カメラ・インターフェイスIPとビットマップ・ディスプレイ・コントローラIPにわかれている。別々に作ってきたと同時にIPを使いまわせるように、こうなったのだが、トリプルバッファリングするにはIPがわかれてしまっている。そこで、トリプルバッファリング用のIPをXPSプロジェクト上に追加することにした。関連する部分のブロック図を下に示す。
Triple_FBC_1_130507.png

Tripp Frame Buffer Controller は、カメラ・インターフェイスIPとビットマップ・ディスプレイ・コントローラIPへFrame Buffer の先頭番地を渡し、カメラ・インターフェイスIPから frame_valid、ビットマップ・ディスプレイ・コントローラIPからは display_enable をもらうこととする。
これで、”画像のフレームバッファ2(ライフサイクルをシミュレーション)”で行ったシミュレーションを実際の回路に実装できると思う。
  1. 2013年05月07日 05:49 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

LチカでAXIバスやAXIカスタムIPの作り方を学ぼうセミナ予定

 内部的にAXIバスやAXIカスタムIPの作り方を学ぶセミナをやろうと思っている。その予定を書いておこうと思う。題して、”LチカでAXIバスやAXIカスタムIPの作り方を学ぼうセミナ”。
今まで、いろいろなセミナに行ったけど、実習の回路が高度だとそっちに気を取られて、肝心の学ぶべき部分をしっかり学べないと思う。そこで、簡単なよくわかっている回路で核心の部分にスポットを当てたいと思っている。

LチカでAXIバスやAXIカスタムIPの作り方を学ぼうセミナ予定
(使用ボード:ZedBoard, Atlys)
1. Xilinxツールの説明

1. ISE
2. PlanAhead
3. EDK
1. XPS
2. SDK
4. Vivado
2. PlanAheadでスイッチを押した時にLチカを行う回路を作る。

3. PlanAheadにEDKプロジェクトを作って、XPSで2つのGPIOを接続し、MicroBlazeのソフトウェアで、スイッチを押した時にLチカする。

4. AXIバスの説明
1. AXI4 Slave, AXI4 Lite Slave, AXI4 Master, AXI4-Stream

5. スイッチ押したことを知らせるAXI4 Master IP(Writeのみ)と知らせがあったらLチカをするAXI4 Lite Slave IP(Readのみ)を作り、XPSプロジェクトで接続して動作させる。MicroBlazeは削除。

6. スイッチのチャタリング除去とenable, sw_status 出力部分を分離して、1つのIPとし、それとスイッチ押したことを知らせるAXI4 Master IPをXPS上で接続する。

7. 4.のAXI4 Master IPにLチカのモニタ機能を付ける。(AXI4 Master Readの実装)

8. 5.にLチカAXI4 Lite Slave IPをLEDの数分だけ搭載する。スイッチ押したことを知らせるAXI4 Master IPは1個とする。AXI4 Master IPにレジスタ領域(AXI4 Lite Slave)を用意して、MicroBlazeも再実装して、レジスタに各LチカAXI4 Lite Slave IPのアドレスを設定する。押したスイッチによって、レジスタに格納されたアドレスにメッセージを送る。


ただし、受講者は予めARMのサイトに登録して、AXIバスのマニュアルをダウンロードして持っている人とする。(2日くらいの日程かな?)
  1. 2013年05月05日 20:56 |
  2. その他のFPGAの話題
  3. | トラックバック:0
  4. | コメント:0

画像のフレームバッファ2(ライフサイクルをシミュレーション)

画像のフレームバッファ”でAXI VDMAのProduct Guide にトリプルバッファでフレームレートの異なるWrite, Read で表示が乱れないようだということを学習した。つまり、カメラ表示システムで言うと、カメラデータのWrite とディスプレイに表示するためのRead のフレームレートが異なっていても、トリプルバッファで切断ラインが出ないように表示することができるということだ。今回は、ツィッターの @ikwzm さんのツィートをご紹介してから、そのアルゴリズムをシミュレーションしてみる。トリプルバッファの各フレームバッファの状態遷移をシミュレーションで見ることにする。

最初に、ツィッターの @ikwzm さんに教えていただいたツィートを下に示す。ツィートの引用を許可していただいてありがとうございました。





このアルゴリズムに従って、トリプルバッファのライフサイクルを模式的にシミュレーションしてみた。
多分、AXI VDMAはWriteストリームクロックとReadストリームクロック、それにAXIバスのクロックと3つのクロックがあるので、推測だが、Write, Read の信号をAXIバスのクロックに載せ替えてステートマシンを組んであるのではないか?と思う。今回のシミュレーションは1つのクロックで動作するので、そのまま実際のハードウェアに持っていくことは出来ないが、各フレームバッファの振る舞いは見ることができる。
さて、それではVerilog HDL ソースから示す。下に、フレームバッファのVerilog HDLファイルの fb_life_cycle.v を下に示す。(D:\HDL\FndtnISEWork\Test\fb_life_cycle)

// frame buffer life cycle
//

`default_nettype none

module fb_life_cycle (
    input    wire    clk,
    input    wire    reset,
    input    wire    write_req,
    input    wire    display_req,
    input    wire    wtime_is_zero,
    input    wire    dtime_is_zero,
    output    reg        [1:0]    state
);
    localparam     wait_write =     2'b00,
                writing    =        2'b01,
                displaying =    2'b10,
                wait_display =    2'b11;
    
    always @(posedge clk) begin
        if (reset)
            state <= wait_write;
        else begin
            case (state)
                wait_write :
                    if (write_req)
                        state <= writing;
                writing :
                    if (write_req)
                        state <= writing;
                    else if (wtime_is_zero)
                        state <= wait_display;
                wait_display :
                    if (display_req)
                        state <= displaying;
                displaying :
                    if (display_req)
                        state <= displaying;
                    else if (dtime_is_zero)
                        state <=wait_write;
            endcase
        end
    end
    
    // synthesis translate_off
     reg [12*8:1] FB_STATE;
     
     always @(state) begin
        case (state)
            wait_write:        FB_STATE <= "WAIT_WRITE";
            writing:        FB_STATE <= "WRITING";
            displaying :    FB_STATE <= "DISPLAYING";
            wait_display :    FB_STATE <= "WAIT_DISPLAY";
        endcase
    end    
    // synthesis translate_on
endmodule

`default_nettype wire    


テストベンチ fb_life_cycle_tb.v を下に示す。9行目と10行目の write_life_cycle, display_life_cycle は画像のWrite, Read のフレームレートを式の分母に代入する。現在の例は、どちらも 60fps の場合だ。

// Frame Buffer Life Cycle Testbench
//

`default_nettype none

`timescale 1us / 100ps

module fb_life_cycle_tb;
    parameter    integer    write_life_cycle    = 100000/60;    // 60fps
    parameter    integer display_life_cycle    = 100000/60;    // 60fps

    localparam     wait_write =     2'b00,
                writing    =        2'b01,
                displaying =    2'b10,
                wait_display =    2'b11;

    wire    clk;
    wire    reset;
    wire    [1:0]    state_1;
    wire    [1:0]    state_2;
    wire    [1:0]    state_3;
    reg        write_req_1, write_req_2, write_req_3;
    reg        display_req_1, display_req_2, display_req_3;
    reg        [23:0] wtime;
    reg        [23:0] dtime;
    wire    wtime_is_zero, dtime_is_zero;
    reg        reset_1b;
    
    // clk のインスタンス
    clk_gen #(
        .CLK_PERIOD(10),    // 10usec, 100kHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) clki (
        .clk_out(clk)
    );
    
    // reset_gen のインスタンス
    reset_gen #(
        .RESET_STATE(1'b1),
        .RESET_TIME(10)    // 10usec
    ) RESETi (
        .reset_out(reset)
    );
    
    // Frame Buffer のインスタンス
    fb_life_cycle fb_life_cycle_1 (
        .clk(clk),
        .reset(reset),
        .write_req(write_req_1),
        .display_req(display_req_1),
        .wtime_is_zero(wtime_is_zero),
        .dtime_is_zero(dtime_is_zero),
        .state(state_1)
    );
    
    fb_life_cycle fb_life_cycle_2 (
        .clk(clk),
        .reset(reset),
        .write_req(write_req_2),
        .display_req(display_req_2),
        .wtime_is_zero(wtime_is_zero),
        .dtime_is_zero(dtime_is_zero),
        .state(state_2)
    );
    
    fb_life_cycle fb_life_cycle_3 (
        .clk(clk),
        .reset(reset),
        .write_req(write_req_3),
        .display_req(display_req_3),
        .wtime_is_zero(wtime_is_zero),
        .dtime_is_zero(dtime_is_zero),
        .state(state_3)
    );
    
    always @(posedge clk) begin
        if (reset)
            wtime <= write_life_cycle - 1;
        else begin
            if (wtime == 24'd0)
                wtime <= write_life_cycle - 1;
            else
                wtime <= wtime - 24'd1;
        end
    end
    assign wtime_is_zero = (wtime == 24'd0) ? 1'b1 : 1'b0;
    
    always @(posedge clk) begin
        if (reset)
            dtime <= display_life_cycle - 1;
        else begin
            if (dtime == 24'd0)
                dtime <= display_life_cycle - 1;
            else
                dtime <= dtime - 24'd1;
        end
    end
    assign dtime_is_zero = (dtime == 24'd0) ? 1'b1 : 1'b0;
    
    always @(posedge clk) begin
        reset_1b <= reset;
    end 
    
    always @* begin
        if (~reset & reset_1b) begin
            write_req_1 <= 1'b1; write_req_2 <= 1'b0; write_req_3 <= 1'b0;
        end else if (wtime_is_zero) begin
            if (state_1 == wait_write) begin
                write_req_1 <= 1'b1; write_req_2 <= 1'b0; write_req_3 <= 1'b0;
            end else if (state_2 == wait_write) begin
                write_req_1 <= 1'b0; write_req_2 <= 1'b1; write_req_3 <= 1'b0;
            end else if (state_3 == wait_write) begin
                write_req_1 <= 1'b0; write_req_2 <= 1'b0; write_req_3 <= 1'b1;
            end else if (state_1==writing && state_2!=wait_write && state_3!=wait_write) begin
                write_req_1 <= 1'b1; write_req_2 <= 1'b0; write_req_3 <= 1'b0;
            end else if (state_2==writing && state_1!=wait_write && state_3!=wait_write) begin
                write_req_1 <= 1'b0; write_req_2 <= 1'b1; write_req_3 <= 1'b0;
            end else if (state_3==writing && state_2!=wait_write && state_3!=wait_write) begin
                write_req_1 <= 1'b0; write_req_2 <= 1'b0; write_req_3 <= 1'b1;
            end else begin
                write_req_1 <= 1'b0; write_req_2 <= 1'b0; write_req_3 <= 1'b0;
            end
        end else begin
            write_req_1 <= 1'b0; write_req_2 <= 1'b0; write_req_3 <= 1'b0;
        end
    end
    
    always @* begin
        if (dtime_is_zero) begin
            if (state_1 == wait_display) begin
                display_req_1 <= 1'b1; display_req_2 <= 1'b0; display_req_3 <= 1'b0;
            end else if (state_2 == wait_display) begin
                display_req_1 <= 1'b0; display_req_2 <= 1'b1; display_req_3 <= 1'b0;
            end else if (state_3 == wait_display) begin
                display_req_1 <= 1'b0; display_req_2 <= 1'b0; display_req_3 <= 1'b1;
            end else if (state_1==displaying && state_2!=wait_display && state_3!=wait_display) begin
                display_req_1 <= 1'b1; display_req_2 <= 1'b0; display_req_3 <= 1'b0;
            end else if (state_2==displaying && state_1!=wait_display && state_3!=wait_display) begin
                display_req_1 <= 1'b0; display_req_2 <= 1'b1; display_req_3 <= 1'b0;
            end else if (state_3==displaying && state_1!=wait_display && state_2!=wait_display) begin
                display_req_1 <= 1'b0; display_req_2 <= 1'b0; display_req_3 <= 1'b1;
            end else begin
                display_req_1 <= 1'b0; display_req_2 <= 1'b0; display_req_3 <= 1'b0;
            end
        end else begin
            display_req_1 <= 1'b0; display_req_2 <= 1'b0; display_req_3 <= 1'b0;
        end
    end

    // synthesis translate_off
    localparam    integer    CHAR_NUM    = 12;
    
    reg [CHAR_NUM*8:1] FB_STATE_1;
    reg [CHAR_NUM*8:1] FB_STATE_2;
    reg [CHAR_NUM*8:1] FB_STATE_3;

    always @(state_1) begin
        case (state_1)
            wait_write:        FB_STATE_1 <= "WAIT_WRITE";
            writing:        FB_STATE_1 <= "WRITING";
            displaying :    FB_STATE_1 <= "DISPLAYING";
            wait_display :    FB_STATE_1 <= "WAIT_DISPLAY";
        endcase
    end

    always @(state_2) begin
        case (state_2)
            wait_write:        FB_STATE_2 <= "WAIT_WRITE";
            writing:        FB_STATE_2 <= "WRITING";
            displaying :    FB_STATE_2 <= "DISPLAYING";
            wait_display :    FB_STATE_2 <= "WAIT_DISPLAY";
        endcase
    end
    
    always @(state_3) begin
        case (state_3)
            wait_write:        FB_STATE_3 <= "WAIT_WRITE";
            writing:        FB_STATE_3 <= "WRITING";
            displaying :    FB_STATE_3 <= "DISPLAYING";
            wait_display :    FB_STATE_3 <= "WAIT_DISPLAY";
        endcase
    end    
    // synthesis translate_on
endmodule
    
module clk_gen #(
    parameter         CLK_PERIOD = 100,
    parameter real    CLK_DUTY_CYCLE = 0.5,
    parameter        CLK_OFFSET = 0,
    parameter        START_STATE    = 1'b0 )
(
    output    reg        clk_out
);
    begin
        initial begin
            #CLK_OFFSET;
            forever
            begin
                clk_out = START_STATE;
                #(CLK_PERIOD-(CLK_PERIOD*CLK_DUTY_CYCLE)) clk_out = ~START_STATE;
                #(CLK_PERIOD*CLK_DUTY_CYCLE);
            end
        end
    end
endmodule

module reset_gen #(
    parameter    RESET_STATE = 1'b1,
    parameter    RESET_TIME = 100 )
(
    output    reg        reset_out
);
    begin
        initial begin
            reset_out = RESET_STATE;
            #RESET_TIME;
            reset_out = ~RESET_STATE;
        end
    end
endmodule

`default_nettype wire


これをISimでシミュレーションしてみた。これが 1sec シミュレーションした結果だ。
TripleBufferLifeCycle_1_130504.png

これだとわからないと思うので、後ろの方を拡大してみた。
TripleBufferLifeCycle_2_130504.png

FB_STATE_1, FB_STATE_2, FB_STATE_3 が各フレームバッファのステートを示す。それを見ると、どれかのフレームバッファが常にDISPLAYING となって表示が途切れないことがわかる。
ただし、DISPLAYINGのステートが本来のフレームレートの2倍に伸びている。これはフレームレートがぴったり同じだからで、どちらかのフレームレートが少しでも違っていれば、表示のフレームレートは本来のフレームレート 60fps になる。
下に、”write_life_cycle = 100000/61;”に変更した例を示す。ここでは、表示のフレームレートが本来の 60fps となった。
TripleBufferLifeCycle_3_130504.png

次に、Write が 30fps の例を下に示す。
TripleBufferLifeCycle_4_130504.png

Write が 15fps の例を下に示す。
TripleBufferLifeCycle_5_130504.png

Write が 12fps の例を下に示す。
TripleBufferLifeCycle_6_130504.png

このようにトリプルバッファリングすると、DISPLAYING が途切れることがなく、しかもWRITING と異なるので、画像に横筋が入ることはないということが推測できる。
  1. 2013年05月04日 05:06 |
  2. 画像処理
  3. | トラックバック:0
  4. | コメント:0

画像のフレームバッファ

私の作ってきたカメラの画像表示装置はシングルバッファでフレームレートを変換している。
Spartan-3A Starter KitでCMOSカメラ・ディスプレイ回路1(ブロック図)
カメラ表示用プラットフォームが完成
ZedBoard用CMOSカメラ回路の作製14(カメラボード改2)

シングルバッファでフレームレートの異なるカメラとディスプレイを同期すると、どんなことが起こるかというと縦に動くものを見ると横に筋が見える。
(ディスプレイのフレームレート)/(カメラのフレームレート)- 1 の数の横筋が見える。(注:カメラのフレームレートがディスプレイよりも低く、カメラのフレームレートの整数倍がディスプレイのフレームレートとする)
これは、ディスプレイが表示している間にカメラの書き換えている部分が通算(ディスプレイのフレームレート)/(カメラのフレームレート)- 1 箇所あって、それが積算して見えるということだと思う。つまり、同期のとれていないオシロスコープで波形が複数見えるのと一緒。
これを解消するには、ダブルバッファリングしてカメラとディスプレイの同期を取る必要があるかな?と考えていて、回路構成も考えていた。でも、AXI VDMAではトリプルバッファリングで任意のフラームレートのカメラとディスプレイを接続できることがわかった(たぶん)。
LogiCORE IP AXI Video Direct Memory Access v5.04a Product Guide PG020 December 18, 2012 の113ページからの”Triple Frame Buffer Example”がそれだ。最初に読んだときは、なんでトリプルバッファで任意のフレームレートの変換ができるかわからなかったが、ツィッターでikwzm さんに教えていただいてガッテンした。(ikzwm さん、ありがとうございました。とってもよく分かりました)
次は、そのフレームバッファのライフサイクルをシミュレーションしてみようと思う。

#ツィッターは偉大だと思う。と言うか、TLの方、いつも教えていただいてありがとうございます。これからも、よろしくお願いします。

VDMAにはそのほかにも、Genlock Synchronizationなどもあって、奥が深そう。よ~く、勉強したいと思う。とっても勉強になる。そして、AXI VDMAを使う方向で行ってみたい。
  1. 2013年05月02日 05:38 |
  2. 画像処理
  3. | トラックバック:0
  4. | コメント:0