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

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

FPGAの部屋

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

MFT2012出展

Maker Faire Tokyo 2012にアクリルサインを出展します。
FPGA-CAFEのレーザー加工機を使ってアクリルに彫刻して、そのアクリルにFusionPCBで作った自作基板を搭載して、7色に光ります。基板には、ストロベリー・リナックスさんのArduino互換基板、ダ・ヴィンチ32U with Arduino Bootloaderが搭載されています。
自作基板には高輝度の赤、青、緑のLEDが5個ずつ15個搭載されています。それをダ・ヴィンチでPWM制御してじわ~っと明るくし、じわ~っと暗くしています。自作基板にはCdSも搭載されていて、暗くなったら明るさを落とすこともできますが、今は使っていません。
展示は、1F, C12 のFabLab Japanの1角でしていますので、MFTにお越しの際はお立ち寄りください。よろしくお願いします。
MFT2012_1_121130.jpg

電子工作的には余り面白くはないですが、レーザー加工ではいろいろ試行錯誤しています。
  1. 2012年11月30日 05:23 |
  2. Make出展
  3. | トラックバック:0
  4. | コメント:0

ZedBoardでHDMI出力14(YCbCr4:2:2 変換)

ZedBoardでHDMI出力13(HDMIに出力できた)”でHDMIでキャラクタを出力することが出来た。
だが、よく見てみると特に青のキャラクタが汚い。下にHDMIで表示したキャラクタの拡大図を図1に示す。
ZedBoard_HDMI_46_121130.jpg
図1 2つのCb, Crの平均を取る方式のHDMI出力画面

青のキャラクタが潰れて、ピンクのキャラクタの後ろに青が出ているのがわかるだろうか?
次にVGA端子から出力したキャラクタ拡大図を図2に示す。
ZedBoard_HDMI_47_121130.jpg
図2 VGAコネクタ出力画面

2つ並べてみると違っているのがよく分かると思う。VGAの方はすっきり青のキャラクタも見えて、HDMIは青のキャラクタがぼけている。これは、YCbCr4:2:2 で色差信号を1/2に圧縮しているためだと思う。
私の色差信号の圧縮方式は、”ZedBoardでHDMI出力10(ADV7511のピクセル入力レジスタ設定)”で示したように、”1つ前のCb, Crの値を残しておいて、現在の値との平均を取る”という方法だった。リファレンス・デザインを見ると下の方式で色差信号を算出していた。

1.1つ前の色差信号 Z-1 の値を0.5倍する。
2.現在の色差信号の値を Z とする。
3.1つ後の色差信号の値 Z+1 を0.5倍する。
4.1.2.3.を合計して、それに 0.5 を掛ける。
4.つまり ((Z-1)*0.5 + Z + (Z+1)*0.5) * 0.5 を行う。


私もこの方式でやってみようと思う。

下にリファレンス・デザインでやってみた結果を示す。
ZedBoard_HDMI_48_121130.jpg
図3 Cb, Crの値を ((Z-1)*0.5 + Z + (Z+1)*0.5) * 0.5 で求めたHDMI出力画面

最初のHDMI出力キャラクタとよーく見比べると、図1には、青の後ろに灰色で影みたいなのが出ているが、図3にはほとんど出ていない。更に図3の黄色のほうが、図1の黄色よりも白くなっている割合が少ないと思う。この様に僅かではあるが、リファレンス・デザインのYCbCr4:2:2 の算出方法のほうが良いようだ。しかし、色差信号を圧縮してないVGA信号には比べるべくもない。コンソール出力にはVGA出力を使ったほうが良さそうだ。HDMI写真やカメラ出力専用ということになると思う。
最後に、以前の方式のYCbCr4:2:2 算出方法を使ったconv_hdmi_out.v は、”ZedBoardでHDMI出力11(ビットマップ・ディスプレイ・コントローラ単体テスト)”に掲載している。リファレンス・デザインの方式でYCbCr4:2:2 を算出したconv_hdmi_out.v を下に載せておく。実際のVerilog HDLコードとしては、((Z-1) + Z * 2 + (Z+1)) / 4 となっている。* 2 は1ビット左シフトで、/ 4 は2ビット右シフトだ。

// BitMap Display Controller for HDMI
// HDMI Interface
// RGBをYCbCrに変換して出力する
// HDMI出力はRGBよりも3クロック遅延する。
// フルHDだと3クロックでは足りない。conv_rgb2ycbcrの中でもう1つFFが必要。
//
// 2012/11/21
// 2012/11/30 : Cb, Cr の算出方法を変更。(cb_d1 + cb_d2*2 + cb_d3)/4
//

`default_nettype none

module conv_hdmi_out (
    input    wire    clk_disp,            // ディスプレイ表示用クロック
    input    wire    reset_disp,            // clk_disp 用リセット
    input    wire    [7:0]    red,
    input    wire    [7:0]    green,
    input    wire    [7:0]    blue,
    input    wire    hsyncx,
    input    wire    vsyncx,
    input    wire    display_enable,
    output    wire    hdmi_clk,
    output    wire    hdmi_vsync,
    output    wire    hdmi_hsync,
    output    reg        hdmi_data_e,
    output    reg     [15:0]    hdmi_data
);
    
    wire    [7:0]    y;
    wire    [7:0]    cb;
    wire    [7:0]    cr;
    reg     [7:0]    y_d1, y_d2;
    reg     [7:0]    cb_d1, cb_d2, cb_d3;
    reg        [9:0]    cb422;
    reg     [7:0]    cr_d1, cr_d2, cr_d3;
    reg        [9:0]    cr422;
    (*S="TRUE"*) reg    hsyncx_d1, hsyncx_d2, hsyncx_d3;
    (*S="TRUE"*) reg    vsyncx_d1, vsyncx_d2, vsyncx_d3;
    (*S="TRUE"*) reg    de_d1, de_d2, de_d3;
    reg        second_pixel;
    wire    [2:0]    de_stat;
    
    // hdmi_clk の出力
    ODDR #(
        .DDR_CLK_EDGE("OPPOSITE_EDGE"),
        .INIT(1'b0),
        .SRTYPE("SYNC")
    ) hdmi_clk_ODDR (
        .R (1'b0),
        .S (1'b0),
        .CE (1'b1),
        .D1 (1'b0),
        .D2 (1'b1),
        .C (clk_disp),
        .Q (hdmi_clk)
    );

    // RGB - YCbCr 変換
    conv_rgb2ycbcr conv_rgb2ycbcr_i (
        .red(red),
        .green(green),
        .blue(blue),
        .y(y),
        .cb(cb),
        .cr(cr)
    );
    
    // 同期信号、displya_enable を遅延
    always @(posedge clk_disp) begin
        if (reset_disp) begin
            hsyncx_d1 <= 1'b1;
            hsyncx_d2 <= 1'b1;
            hsyncx_d3 <= 1'b1;
            vsyncx_d1 <= 1'b1;
            vsyncx_d2 <= 1'b1;
            vsyncx_d3 <= 1'b1;
            de_d1 <= 1'b0;
            de_d2 <= 1'b0;
        end else begin
            hsyncx_d1 <= hsyncx;
            hsyncx_d2 <= hsyncx_d1;
            hsyncx_d3 <= hsyncx_d2;
            vsyncx_d1 <= vsyncx;
            vsyncx_d2 <= vsyncx_d1;
            vsyncx_d3 <= vsyncx_d2;
            de_d1 <= display_enable;
            de_d2 <= de_d1;
            de_d3 <= de_d2;
        end
    end
    
    // YCbCr のラッチ
    always @(posedge clk_disp) begin
        if (reset_disp) begin
            y_d1 <= 8'd16;
            y_d2 <= 8'd16;
            cb_d1 <= 8'd128;
            cb_d2 <= 8'd128;
            cb_d3 <= 8'd128;
            cr_d1 <= 8'd128;
            cr_d2 <= 8'd128;
            cr_d3 <= 8'd128;
        end else begin
            y_d1 <= y;
            y_d2 <= y_d1;
            cb_d1 <= cb;
            cb_d2 <= cb_d1;
            cb_d3 <= cb_d2;
            cr_d1 <= cr;
            cr_d2 <= cr_d1;
            cr_d3 <= cr_d2;
        end
    end
    
    assign de_stat = {de_d1, de_d2, de_d3};
    
    // Cb の値を算出する
    always @* begin
        case (de_stat)
            3'b111 : // 通常の場合
                cb422 <= {2'b00, cb_d1} + {1'b0, cb_d2, 1'b0} + {2'b00, cb_d3};
            3'b110 : // de が1になった時
                cb422 <= {1'b0, cb_d2, 1'b0} + {1'b0, cb_d1, 1'b0};
            3'b011 : // deの終わりから2つ前
                cb422 <= {1'b0, cb_d2, 1'b0} + {1'b0, cb_d3, 1'b0};
            default :
                cb422 <= 10'd512; // 128*4
        endcase
    end
    
    // Cr の平均を取る
    always @* begin
        case (de_stat)
            3'b111 : // 通常の場合
                cr422 <= {2'b00, cr_d1} + {1'b0, cr_d2, 1'b0} + {2'b00, cr_d3};
            3'b110 : // de が1になった時
                cr422 <= {1'b0, cr_d2, 1'b0} + {1'b0, cr_d1, 1'b0};
            3'b011 : // deの終わりから2つ前
                cr422 <= {1'b0, cr_d2, 1'b0} + {1'b0, cr_d3, 1'b0};
            default :
                cr422 <= 10'd512; // 128*4
        endcase
    end
    
    // hdmi_data の処理
    always @(posedge clk_disp) begin
        if (reset_disp)
            hdmi_data <= 16'd0;
        else begin
            if (de_d2) begin
                if (~second_pixel)
                    hdmi_data <= {y_d2, cb422[9:2]}; // cb422 / 4
                else
                    hdmi_data <= {y_d2, cr422[9:2]}; // cr422 / 4
            end else
                hdmi_data <= 16'd0;
        end
    end
    
    // second_pixel の処理
    always @(posedge clk_disp) begin
        if (reset_disp)
            second_pixel <= 1'b0;
        else begin
            if (de_d2)
                second_pixel<= ~second_pixel;
            else
                second_pixel <= 1'b0;
        end
    end
    
    // hdmi_data_e の処理
    always @(posedge clk_disp) begin
        if (reset_disp)
            hdmi_data_e <= 1'b0;
        else
            hdmi_data_e <= de_d2;
    end;
    
    assign hdmi_hsync = hsyncx_d3;
    assign hdmi_vsync = vsyncx_d3;
    
endmodule

`default_nettype wire


  1. 2012年11月30日 05:06 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

HSYNCとVSYNCをIOBレジスタに入れる

”ZedBoardでHDMI出力13(HDMIに出力できた)”のプロジェクトをFPGA Editor でみたところ、hdmi_hsync, hdmi_vsync, vga_hsync, vga_vsync もデータもIOBのレジスタを使用していなかった。

全体像を説明するために、話をHSYNCやVSYNCに絞って、そのブロック図を図1 に示す。
ZedBoard_HDMI_42_121129.png
図1 HSYNC, VSYNC出力ブロック図

bitmap_disp_cntrler_axi_master.v がビットマップ・ディスプレイ・コントローラのトップだ。その下に、bitmap_disp_engine.v とconv_hdmi_out.v がある。bitmap_disp_engine.v にはTiming Generatorがあって、HSYNCやVSYNCを生成している。それをFFを介してbitmap_disp_cntrler_axi_master.v に出力して、それをconv_hdmi_out.v に入力する。conv_hdmi_out.v では、YCbCr4:2:2 変換を行うために2クロック分遅延するので、その分をHSYNC, VSYNCも遅延させるために2段のFFを通している。bitmap_disp_engine.v のHSYNC, VSYNCは、conv_hdmi_out.v に渡しているので、このままではIOBレジスタを使用できない。その為、もう1段FFを介してFPGA外に出力している。

最初に、PlanAheadでFlow navigator のImplementation のImplementation Settings をクリックして、Mapのオプションを見た。none になっていたので、oに変更した。(グローバル・オプションで出力の最後のFFをIOBレジスタにマップするオプション)
ZedBoard_HDMI_43_121129.png

これでインプリメントしてみたところ、HDMI関連はIOBレジスタを使用したが、vga_hsync, vga_vsync はIOBレジスタを使えていなかった。

次に、UCFファイルで、vga_hsync, vga_vsync, hdmi_hsync, hdmi_vsync にIOB = FORCE 制約を試してみたが、なぜか効かなかった?

そこで、Verilog HDLソースファイルのポート宣言に(* IOB = "FORCE" *)を追加した。

(* IOB = "FORCE" *) output reg vga_hsync,
(* IOB = "FORCE" *) output reg vga_vsync,
(* IOB = "FORCE" *) output wire hdmi_vsync,
(* IOB = "FORCE" *) output wire hdmi_hsync,


これでやっと、vga_hsync, vga_vsyncがIOBレジスタに入らないとのエラーが出るようになった。どうやら、図1のFF2とFF3が統合されてしまっているようだ。これを防ぐためには、Synthhesis オプションの equivalent_register_removal をyes から no にすれば良いと思うのだが、全体にかかってしまうためやらない。

図1のFF3, FF4にS属性(S制約)を掛けてFFをキープした。

(*S="TRUE"*) reg hsyncx_d1, hsyncx_d2;
(*S="TRUE"*) reg vsyncx_d1, vsyncx_d2;


これでも、やはり、vga_hsync, vga_vsyncがIOBレジスタに入らないとのエラーが出る。

FF2にS属性(S制約)を掛ければ良いのだが、すでにIOB = "FORCE"制約を掛けているので、2つ制約が書けるかどうか分からなかった。(1回、やってみるのに時間が掛かるんです)試しに、FF1にS属性(S制約)を掛けてみた。

(*S="TRUE"*) output reg hsyncx,
(*S="TRUE"*) output reg vsyncx,


これでインプリメントしてみたところ、うまく、全部のポートでIOBレジスタを使うことができるようになった。
ZedBoard_HDMI_44_121129.png

ZedBoard_HDMI_45_121129.png

本当は出力ポートのvga_hsync, vga_vsync をreg で宣言してFFにするよりも、1つ前にノードをreg で宣言して、FF記述をして、そこをS属性(S制約)を掛ける。そして、そこから出力ポートに結んで、そこに、 IOB = "FORCE" 制約を掛けたほうが良いと思われる。
  1. 2012年11月29日 05:51 |
  2. 制約
  3. | トラックバック:0
  4. | コメント:0

ZedBoardでHDMI出力13(HDMIに出力できた)

前回、キャラクタは表示できても色がおかしかった。その状況から、RGB - YCbCr変換がおかしいのか?と疑ったが、変換はおかしくないようだった。
その次に、YUV変換なのかな?と思っていたが、ツイッターでY, Cb,Crの値を教えていただいたので、現在のRGB - YCbCr変換はおかしくないようだということがわかった。
次にADV7511 Programming Guid Rev G のTable 18 YCbCr 4:2:2 Formats (24, 20, or 16 bits) Input Data Mapping: を見ると、現在はStyle 2, 16bit(赤の四角で囲った部分) で使用しているが、24bit, 20bit はCb, Crの方が先でYが後なのに、16bitだけYが先になっている。どうも怪しい。この図で行くと16bit の部分もCb, CrとYというフォーマットじゃないだろうか?と思った。つまり、Style 3(ピンクで囲った部分)にする必要があるのでは?と思った。下にTable 18を引用させていただく。
ZedBoard_HDMI_37_121127.png

(こちらの回路では、YとCb, Crを連結してhdmi_data[15:0] に出力している)

そこで、Style 3に設定した。具体的にはADV7511のレジスタ設定の0x16番地を0xb5 から0xbd に変更した。”ZedBoardでHDMI出力7(ADV7511のレジスタ設定1)”を参照。

iic_write(0x39, 0x16, 0xbd); // [3:2] -> Input Style => 11 = style 3


その他の設定はリファレンス・デザインの通りにした。そうすると、ちゃんとした色で出力できた。マニュアルが間違っていたようだ。
ZedBoard_HDMI_38_121127.jpg

良かった。嬉しいです。
リファレンス・デザインはどうやって出力しているか見るために、Verilog HDLコードを見てみたが、やはり、Cb, Crが上位ビットでYが下位ビットだった。本当に人騒がせなマニュアルだ。(怒怒怒)
リファレンス・デザインでは、cf_ss_444to422.v の123行目~134行目に、YCbCr422の出力部分がある。

次にDVIでも出力できるのか?を探るために、HDMIからDVIにモードを変更してみた。

iic_write(0x39, 0xaf, 0x04); // [1] -> HDMI/DVI Mode Select => 0 = DVI Mode
// iic_write(0x39, 0xaf, 0x06); // [1] -> HDMI/DVI Mode Select => 1 = HDMI Mode


そうすると、また違ったように色がおかしくなってしまった。
ZedBoard_HDMI_39_121127.jpg

ADV7511 Programming Guid Rev G の19ページ、4.2.2 HDMI DVI Selection を見ると、” DVI only supports the RGB color space”だそうだ。YCbCrはサポート無しとのことだった。もう一度、HDMIモードに変更した。

ZedBoardでVGAサイズの画像をHDMI出力することが出来た。
  1. 2012年11月27日 04:22 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

松島観光

11月23日の勤労感謝の日の金曜日に仙台に行って、松島観光に行って来ました。

仙台で娘を拾って、東北道の仙台宮城インターチェンジから北に向かい、富谷ジャンクションから仙台北部道路に、利府ジャンクションから三陸自動車道に乗り換えて、松島海岸インターチェンジで降りました。松島に向かい、町の中に入りましたが、結構道が混んでいたので、1日500円の駐車場に早々と入れて歩いて行きました。
matsushima_1_121125.jpg

11時30分発の松島湾一周の船 仁王丸に乗って50分間の松島湾一周の旅に出ました。値段は1人1,400円でしたが、2階席は暖房が入っていて、グリーンシートということで、1人あたり600円取られました。なんか、ぼられている気がしたんですが、仕方が無いですね。
matsushima_2_121125.jpg

船に乗るとかもめ?(ウミネコですか?)が寄って来ました。かもめの餌はかっぱえびせんでした。かっぱえびせんを手に腕を伸ばしていると勝手にかっぱらっていきます。(方言?泥棒していく)
matsushima_3_121125.jpg

かもめの数が半端じゃないです。怖いくらい。
matsushima_4_121125.jpg

かもめが凄かったですが、本当は島めぐりです。松島には260以上の島があるそうです。これが天然の防波堤となって、津波をだいぶ防いでくれたそうです。これは、仁王島です。(たぶん)
matsushima_5_121125.jpg

これは鐘島かな?4つの洞窟が見えます。
matsushima_6_121125.jpg

他にも小学校がある島がありました。去年の地震による津波は大丈夫だったんでしょうか?
matsushima_7_121125.jpg

降りてから、海鮮を出す食堂の南部家に入りました。私はカキ丼を頼みました。カキが小ぶりでした。
matsushima_8_121125.jpg

今年の牡蠣は出来が良くなかったという情報もあるんでそうなんでしょうか?奥さんは生牡蠣を食べたんですが、あまり美味しくなかったとのことです。那珂湊の巨大な牡蠣の方が美味しかったそうです。
matsushima_9_121125.jpg

食事の後は五大堂に行きました。ここも海につきだした島の様なところにありました。
matsushima_10_121125.jpg

赤い橋をわたって島に行って来ました。
matsushima_11_121125.jpg

島は公園になっていて、展望台から他の島が見えます。結構歩いて疲れました。
matsushima_12_121125.jpg

その後、ずんだ餅を食べてから瑞巌寺に行きました。
matsushima_13_121125.jpg

本堂は震災で壊れて工事中でしたが、伊達政宗の位牌があって見てきました。立派ですね。中は撮影禁止なので撮影していません。
matsushima_14_121125.jpg

宝物殿もあって、見応えがありました。この日は仙台の娘の所に泊まって来ました。
  1. 2012年11月25日 10:49 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

ZedBoardでHDMI出力12(ビットマップ・ディスプレイ・コントローラのインプリメント)

ZedBoardでHDMI出力付き11(ビットマップ・ディスプレイ・コントローラ単体テスト)”でHDMI出力付きビットマップ・ディスプレイ・コントローラ (BDC) の単体テストが終了した。
今回は、HDMI出力付きBDC のインプリメントを行う。使用しているバージョンはPlanAhead 14.3。
最初に、axi_iic を追加した。ProjectメニューからRescan Usre Repositories を行なって、BDC IPのリスキャンをして、HDMI関連のポートをXPSが認識できるようにした。
XPSのBus Interface タブの画面を下に示す。
ZedBoard_HDMI_32_121125.png

Ports タブの画面を下に示す。HDMI出力関連のポートが増えている。
ZedBoard_HDMI_33_121125.png

Address タブの画面を下に示す。
ZedBoard_HDMI_34_121125.png

XPS を離れて、PlanAheadに戻って論理合成、インプリメントを行った。最後にエラーが出てしまった。
ZedBoard_HDMI_35_121125.png

エラーは、Bit file generation で出ていて、Bad time stamp エラーということだった。検索すると、”ISE 14.3 - 「ERROR:Bitstream:188 - Bad time stamp"」というエラー メッセージが表示される”が見つかった。それによると、エラーは出るがビットストリームは生成されているそうだ。日本語設定になってるためにエラーが出るとのことだった。
このバグもあるけど、PlanAhead 14.3 は上図でわかるように、Hierarchyウインドウが頻繁にUpdating Hierarchy...になってしまって、階層が表示されない。他のウインドウ (Compile Order) などにすると問題ないので、Hierarchyウインドウの問題のようだ。

次に、ハードウェアをSDKにエクスポートしてSDKを立ち上げた。
リファレンス・デザインのADV7511の設定をHDMIからDVIに変更してやってみたところ、HDMIから出力はされたのだが、色がおかしくなってしまった。VGAへの出力の色は問題ない。現在の解像度はVGA画像(640x480)で、ピクセルクロックは25MHzだ。
ZedBoard_HDMI_36_121125.jpg

なぜそうなったのか?分からない。調査中。
  1. 2012年11月25日 05:30 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

ZedBoardでHDMI出力11(ビットマップ・ディスプレイ・コントローラ単体テスト)

前回までの調査で、ADV7511のことが大体わかったので、HDMI出力回路をビットマップ・ディスプレイ・コントローラに付けることにした。
bitmap_disp_cntrler_axi_master.v にconv_hdmi_out.v を追加した。bitmap_disp_cntrler_axi_master.v を下に示す。

// bitmap_disp_cntrler_axi_master.v 
//
// Read Only IP, 64 bit bus
//
// 2012/06/28
// 2012/11/22 : HDMI出力を追加

`default_nettype none

module bitmap_disp_cntrler_axi_master #
  (
        parameter integer C_INTERCONNECT_M_AXI_WRITE_ISSUING = 8,
        parameter integer C_M_AXI_THREAD_ID_WIDTH       = 1,
        parameter integer C_M_AXI_ADDR_WIDTH            = 32,
        parameter integer C_M_AXI_DATA_WIDTH            = 64,
        parameter integer C_M_AXI_AWUSER_WIDTH          = 1,
        parameter integer C_M_AXI_ARUSER_WIDTH          = 1,
        parameter integer C_M_AXI_WUSER_WIDTH           = 1,
        parameter integer C_M_AXI_RUSER_WIDTH           = 1,
        parameter integer C_M_AXI_BUSER_WIDTH           = 1,
        parameter [31:0]  C_M_AXI_TARGET                 = 32'h00000000,
        parameter integer C_M_AXI_BURST_LEN                = 256,
        parameter integer C_OFFSET_WIDTH                = 32,
        
        /* Disabling these parameters will remove any throttling.
        The resulting ERROR flag will not be useful */ 
        parameter integer C_M_AXI_SUPPORTS_WRITE         = 0,
        parameter integer C_M_AXI_SUPPORTS_READ         = 1
    )
    (
        // System Signals
        input wire           ACLK,
        input wire           ARESETN,

        // Master Interface Write Address
        output wire [C_M_AXI_THREAD_ID_WIDTH-1:0] M_AXI_AWID,
        output wire [C_M_AXI_ADDR_WIDTH-1:0]      M_AXI_AWADDR,
        output wire [8-1:0]              M_AXI_AWLEN,
        output wire [3-1:0]              M_AXI_AWSIZE,
        output wire [2-1:0]              M_AXI_AWBURST,
        output wire                  M_AXI_AWLOCK,
        output wire [4-1:0]              M_AXI_AWCACHE,
        output wire [3-1:0]              M_AXI_AWPROT,
        // AXI3 output wire [4-1:0]                  M_AXI_AWREGION,
        output wire [4-1:0]              M_AXI_AWQOS,
        output wire [C_M_AXI_AWUSER_WIDTH-1:0]      M_AXI_AWUSER,
        output wire                  M_AXI_AWVALID,
        input  wire                  M_AXI_AWREADY,

        // Master Interface Write Data
        // AXI3 output wire [C_M_AXI_THREAD_ID_WIDTH-1:0]     M_AXI_WID,
        output wire [C_M_AXI_DATA_WIDTH-1:0]      M_AXI_WDATA,
        output wire [C_M_AXI_DATA_WIDTH/8-1:0]      M_AXI_WSTRB,
        output wire                  M_AXI_WLAST,
        output wire [C_M_AXI_WUSER_WIDTH-1:0]      M_AXI_WUSER,
        output wire                  M_AXI_WVALID,
        input  wire                  M_AXI_WREADY,

        // Master Interface Write Response
        input  wire [C_M_AXI_THREAD_ID_WIDTH-1:0]      M_AXI_BID,
        input  wire [2-1:0]              M_AXI_BRESP,
        input  wire [C_M_AXI_BUSER_WIDTH-1:0]      M_AXI_BUSER,
        input  wire                  M_AXI_BVALID,
        output wire                  M_AXI_BREADY,

        // Master Interface Read Address
        output wire [C_M_AXI_THREAD_ID_WIDTH-1:0]      M_AXI_ARID,
        output wire [C_M_AXI_ADDR_WIDTH-1:0]      M_AXI_ARADDR,
        output wire [8-1:0]              M_AXI_ARLEN,
        output wire [3-1:0]              M_AXI_ARSIZE,
        output wire [2-1:0]              M_AXI_ARBURST,
        output wire [2-1:0]              M_AXI_ARLOCK,
        output wire [4-1:0]              M_AXI_ARCACHE,
        output wire [3-1:0]              M_AXI_ARPROT,
        // AXI3 output wire [4-1:0]          M_AXI_ARREGION,
        output wire [4-1:0]              M_AXI_ARQOS,
        output wire [C_M_AXI_ARUSER_WIDTH-1:0]      M_AXI_ARUSER,
        output wire                  M_AXI_ARVALID,
        input  wire                  M_AXI_ARREADY,

        // Master Interface Read Data 
        input  wire [C_M_AXI_THREAD_ID_WIDTH-1:0]      M_AXI_RID,
        input  wire [C_M_AXI_DATA_WIDTH-1:0]      M_AXI_RDATA,
        input  wire [2-1:0]              M_AXI_RRESP,
        input  wire                  M_AXI_RLAST,
        input  wire [C_M_AXI_RUSER_WIDTH-1:0]      M_AXI_RUSER,
        input  wire                  M_AXI_RVALID,
        output wire                  M_AXI_RREADY,
        
        // User Ports
        input    wire    pixclk,
        
        output    wire [3:0]    vga_red,
        output    wire [3:0]    vga_green,
        output    wire [3:0]    vga_blue,
        output    wire    vga_hsync,
        output    wire    vga_vsync,
        
        output    wire    hdmi_clk,
        output    wire    hdmi_vsync,
        output    wire    hdmi_hsync,
        output    wire    hdmi_data_e,
        output    wire    [15:0]    hdmi_data,
        
        input    wire    init_done
    );
    
    wire    [7:0]    red, green, blue;
    wire    hsyncx, vsyncx;
    wire    display_enable;
    wire    bde_req, bde_ack;
    wire    [7:0]    bde_arlen;
    wire    [31:0]    bde_address;
    wire    [63:0]    bde_data;
    wire    bde_data_valid;
    reg        reset_disp_2b, reset_disp_1b;
    wire    reset_disp;
    wire    afifo_overflow, afifo_underflow;
    wire    addr_is_zero, h_v_is_zero;
    
    axi_master_interface #(
        .C_M_AXI_THREAD_ID_WIDTH(C_M_AXI_THREAD_ID_WIDTH),
        .C_M_AXI_ADDR_WIDTH(C_M_AXI_ADDR_WIDTH),
        .C_M_AXI_DATA_WIDTH(C_M_AXI_DATA_WIDTH),
        .C_M_AXI_AWUSER_WIDTH(C_M_AXI_AWUSER_WIDTH),
        .C_M_AXI_ARUSER_WIDTH(C_M_AXI_ARUSER_WIDTH),
        .C_M_AXI_WUSER_WIDTH(C_M_AXI_WUSER_WIDTH),
        .C_M_AXI_RUSER_WIDTH(C_M_AXI_RUSER_WIDTH),
        .C_M_AXI_BUSER_WIDTH(C_M_AXI_BUSER_WIDTH),
        .C_M_AXI_SUPPORTS_WRITE(C_M_AXI_SUPPORTS_WRITE),
        .C_M_AXI_SUPPORTS_READ(C_M_AXI_SUPPORTS_READ)
    ) axi_master_inf_inst
    (
        .ACLK(ACLK), 
        .ARESETN(ARESETN), 
        .M_AXI_AWID(M_AXI_AWID), 
        .M_AXI_AWADDR(M_AXI_AWADDR), 
        .M_AXI_AWLEN(M_AXI_AWLEN), 
        .M_AXI_AWSIZE(M_AXI_AWSIZE), 
        .M_AXI_AWBURST(M_AXI_AWBURST), 
        .M_AXI_AWLOCK(M_AXI_AWLOCK), 
        .M_AXI_AWCACHE(M_AXI_AWCACHE), 
        .M_AXI_AWPROT(M_AXI_AWPROT), 
        .M_AXI_AWQOS(M_AXI_AWQOS), 
        .M_AXI_AWUSER(M_AXI_AWUSER), 
        .M_AXI_AWVALID(M_AXI_AWVALID), 
        .M_AXI_AWREADY(M_AXI_AWREADY), 
        .M_AXI_WDATA(M_AXI_WDATA), 
        .M_AXI_WSTRB(M_AXI_WSTRB), 
        .M_AXI_WLAST(M_AXI_WLAST), 
        .M_AXI_WUSER(M_AXI_WUSER), 
        .M_AXI_WVALID(M_AXI_WVALID), 
        .M_AXI_WREADY(M_AXI_WREADY), 
        .M_AXI_BID(M_AXI_BID), 
        .M_AXI_BRESP(M_AXI_BRESP), 
        .M_AXI_BUSER(M_AXI_BUSER), 
        .M_AXI_BVALID(M_AXI_BVALID), 
        .M_AXI_BREADY(M_AXI_BREADY), 
        .M_AXI_ARID(M_AXI_ARID), 
        .M_AXI_ARADDR(M_AXI_ARADDR), 
        .M_AXI_ARLEN(M_AXI_ARLEN), 
        .M_AXI_ARSIZE(M_AXI_ARSIZE), 
        .M_AXI_ARBURST(M_AXI_ARBURST), 
        .M_AXI_ARLOCK(M_AXI_ARLOCK), 
        .M_AXI_ARCACHE(M_AXI_ARCACHE), 
        .M_AXI_ARPROT(M_AXI_ARPROT), 
        .M_AXI_ARQOS(M_AXI_ARQOS), 
        .M_AXI_ARUSER(M_AXI_ARUSER), 
        .M_AXI_ARVALID(M_AXI_ARVALID), 
        .M_AXI_ARREADY(M_AXI_ARREADY), 
        .M_AXI_RID(M_AXI_RID), 
        .M_AXI_RDATA(M_AXI_RDATA), 
        .M_AXI_RRESP(M_AXI_RRESP), 
        .M_AXI_RLAST(M_AXI_RLAST), 
        .M_AXI_RUSER(M_AXI_RUSER), 
        .M_AXI_RVALID(M_AXI_RVALID), 
        .M_AXI_RREADY(M_AXI_RREADY), 
        
        .bde_req(bde_req),
        .bde_ack(bde_ack),
        .bde_arlen(bde_arlen),
        .bde_address(bde_address),
        .bde_data_out(bde_data),
        .bde_data_valid(bde_data_valid)
    );
    
    bitmap_disp_engine bitmap_disp_eng_inst (
        .clk_disp(pixclk),
        .clk_axi(ACLK),
        .reset_disp(reset_disp),
        .reset_axi(~ARESETN),
        .req(bde_req),
        .ack(bde_ack),
        .ARLEN(bde_arlen),
        .address(bde_address),
        .data_in(bde_data),
        .data_valid(bde_data_valid),
        .red_out(red),
        .green_out(green),
        .blue_out(blue),
        .hsyncx(hsyncx),
        .vsyncx(vsyncx),
        .display_enable(display_enable),
        .ddr_cont_init_done(init_done),
        .afifo_overflow(afifo_overflow),
        .afifo_underflow(afifo_underflow),
        .addr_is_zero(addr_is_zero),
        .h_v_is_zero(h_v_is_zero)
    );
    
    assign vga_red = red[7:4];
    assign vga_green = green[7:4];
    assign vga_blue = blue[7:4];
    assign vga_hsync = hsyncx;
    assign vga_vsync = vsyncx;
    
    always @(posedge pixclk) begin
        reset_disp_2b <= ~ARESETN;
        reset_disp_1b <= reset_disp_2b;
    end
    assign reset_disp = reset_disp_1b;
    
    conv_hdmi_out conv_hdmi_out_inst (
        .clk_disp(pixclk),
        .reset_disp(reset_disp),
        .red(red),
        .green(green),
        .blue(blue),
        .hsyncx(hsyncx),
        .vsyncx(vsyncx),
        .display_enable(display_enable),
        .hdmi_clk(hdmi_clk),
        .hdmi_vsync(hdmi_vsync),
        .hdmi_hsync(hdmi_hsync),
        .hdmi_data_e(hdmi_data_e),
        .hdmi_data(hdmi_data)
    );
    
endmodule

`default_nettype wire


次に、conv_hdmi_out.v を下に示す。単体テストではOKになったが、まだバグがあるかも知れない?
Cb とCr の4:2:2への変換方法は、前回書いた様に前のピクセルの値との平均値を取っている。
(2012/11/26: second_pixel の処理が抜けていたので、修正しました)

// BitMap Display Controller for HDMI
// HDMI Interface
// RGBをYCbCrに変換して出力する
// HDMI出力はRGBよりも3クロック遅延する。
// フルHDだと3クロックでは足りない。conv_rgb2ycbcrの中でもう1つFFが必要。
//
// 2012/11/21
//

`default_nettype none

module conv_hdmi_out (
    input    wire    clk_disp,            // ディスプレイ表示用クロック
    input    wire    reset_disp,            // clk_disp 用リセット
    input    wire    [7:0]    red,
    input    wire    [7:0]    green,
    input    wire    [7:0]    blue,
    input    wire    hsyncx,
    input    wire    vsyncx,
    input    wire    display_enable,
    output    wire    hdmi_clk,
    output    wire    hdmi_vsync,
    output    wire    hdmi_hsync,
    output    wire    hdmi_data_e,
    output    reg     [15:0]    hdmi_data
);
    
    wire    [7:0]    y;
    wire    [7:0]    cb;
    wire    [7:0]    cr;
    reg     [7:0]    y_d1;
    reg     [7:0]    cb_d1, cb_d2;
    reg        [8:0]    cb422;
    reg     [7:0]    cr_d1, cr_d2;
    reg        [8:0]    cr422;
    reg        hsyncx_d1, hsyncx_d2;
    reg        vsyncx_d1, vsyncx_d2;
    reg        de_d1, de_d2;
    reg        second_pixel;
    
    // hdmi_clk の出力
    ODDR #(
        .DDR_CLK_EDGE("OPPOSITE_EDGE"),
        .INIT(1'b0),
        .SRTYPE("SYNC")
    ) hdmi_clk_ODDR (
        .R (1'b0),
        .S (1'b0),
        .CE (1'b1),
        .D1 (1'b0),
        .D2 (1'b1),
        .C (clk_disp),
        .Q (hdmi_clk)
    );

    // RGB - YCbCr 変換
    conv_rgb2ycbcr conv_rgb2ycbcr_i (
        .red(red),
        .green(green),
        .blue(blue),
        .y(y),
        .cb(cb),
        .cr(cr)
    );
    
    // 同期信号、displya_enable を遅延
    always @(posedge clk_disp) begin
        if (reset_disp) begin
            hsyncx_d1 <= 1'b1;
            hsyncx_d2 <= 1'b1;
            vsyncx_d1 <= 1'b1;
            vsyncx_d2 <= 1'b1;
            de_d1 <= 1'b0;
            de_d2 <= 1'b0;
        end else begin
            hsyncx_d1 <= hsyncx;
            hsyncx_d2 <= hsyncx_d1;
            vsyncx_d1 <= vsyncx;
            vsyncx_d2 <= vsyncx_d1;
            de_d1 <= display_enable;
            de_d2 <= de_d1;
        end
    end
    
    // YCbCr のラッチ
    always @(posedge clk_disp) begin
        if (reset_disp) begin
            y_d1 <= 8'd16;
            cb_d1 <= 8'd128;
            cb_d2 <= 8'd128;
            cr_d1 <= 8'd128;
            cr_d2 <= 8'd128;
        end else begin
            y_d1 <= y;
            cb_d1 <= cb;
            cb_d2 <= cb_d1;
            cr_d1 <= cr;
            cr_d2 <= cr_d1;
        end
    end
    
    // Cb の平均を取る
    always @* begin
        if (de_d1) begin
            if (de_d1==1'b1 && de_d2==1'b0) // ラインの最初の画素
                cb422 <= {1'b0, cb_d1} + {1'b0, cb_d1}; // 最初のCbの値はそのまま平均値とする
            else
                cb422 <= {1'b0, cb_d1} + {1'b0, cb_d2};
        end else
            cb422 <= 9'd256; // 128*2
    end
    
    // Cr の平均を取る
    always @* begin
        if (de_d1) begin
            if (de_d1==1'b1 && de_d2==1'b0) // ラインの最初の画素
                cr422 <= {1'b0, cr_d1} + {1'b0, cr_d1};    // 最初のCrの値はそのまま平均値とする
            else
                cr422 <= {1'b0, cr_d1} + {1'b0, cr_d2};
        end else
            cr422 <= 9'd256; // 128*2
    end
    
    // hdmi_data の処理
    always @(posedge clk_disp) begin
        if (reset_disp)
            hdmi_data <= 16'd0;
        else begin
            if (de_d1) begin
                if (~second_pixel)
                    hdmi_data <= {y_d1, cb422[8:1]};
                else
                    hdmi_data <= {y_d1, cr422[8:1]};
            end else
                hdmi_data <= 16'd0;
        end
    end
    
    // second_pixel の処理
    always @(posedge clk_disp) begin
        if (reset_disp)
            second_pixel <= 1'b0;
        else begin
            if (de_d1)
                second_pixel<= ~second_pixel;
            else
                second_pixel <= 1'b0;
        end
    end
    
    assign hdmi_data_e = de_d2;
    assign hdmi_hsync = hsyncx_d2;
    assign hdmi_vsync = vsyncx_d2;
    
endmodule

`default_nettype wire


conv_rgb2ycbcr.v は”RGB―YCbCr変換の検討3(切り捨てと0捨1入)”で決定したように、0捨1入の RGB - YCbCr変換を使用している。

これで、bitmap_disp_cntrler_axi_master.v をトップとして、テストベンチ・ファイルのbitmap_disp_cntrler_axi_master_tb.v を追加したProject Navigator のプロジェクトを作製した。
ZedBoard_HDMI_29_121122.png

下にテストベンチのシミュレーション波形を示す。これは時刻 0ns からのスタートだ。
M_AXI_ARVALIDにM_AXI_ARREADYが応答して、M_AXI_RVALIDが1になったら、M_AXI_RREADYを1にしてデータを受け取っている。
ZedBoard_HDMI_30_121122.png

次に、RGB - YCbCr変換のconv_rgb2ycbcr.v を見てみよう。red, green, blue に対して、hdmi_data が2クロック遅れて出力されている。
ZedBoard_HDMI_31_121122.png
  1. 2012年11月22日 05:42 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

ZedBoardでHDMI出力10(ADV7511のピクセル入力レジスタ設定)

ZedBoardでHDMI出力7(ADV7511のレジスタ設定1)”、”ZedBoardでHDMI出力8(ADV7511のレジスタ設定2)”、”ZedBoardでHDMI出力9(ADV7511のレジスタ設定3)”で、ANALOG DEVICESのリファレンス・デザインにおける画像の主要なADV7511のレジスタ設定を見た。

今回は自分でADV7511を使うという視点からADV7511にピクセルデータを入力する信号についてまとめていきたいと思う。

・16ビット長のYCbCr4:2:2を入力する。
 ・設定レジスタの0x16番地 [7] Output Format 1= 4:2:2
 ・設定レジスタの0x16番地 [5:4] Color Depth 11 = 8bit(これでADV7511のデータ幅が16ビット決定)
 ・設定レジスタの0x16番地 [3:2] Input Style 01 = style 2
 ・設定レジスタの0x16番地 [0] Output Colorspace for Black Image 1 = YCbCr

・YCbCrの入力方法は”ADV7511 Low-Power HDMI 1.4 Compatible Transmitter with Audio Return Channel PROGRAMMING GUIDE - Revision G– March 2012 ”(このマニュアルは、ADV7511 Design Support Filesからダウンロードできる)の”Table 18 YCbCr 4:2:2 Formats (24, 20, or 16 bits) Input Data Mapping: 0x48[4:3] = ‘01’ (right justified) Input ID = 1 or 2”から調べることができる。
Table 18の一部を下に引用する。
ZedBoard_HDMI_28_121120.png

上の表でピンクで囲ったYCbCr4:2:2 16ビットデータ幅となる。つまり、最初の16ビットはY, Cb、次の16ビットはY, Crと出力する。

・ここで問題となるのは、YはRGB - YCbCr変換のY出力をそのまま使用するが、Cb, Cr はどうするか?ということになる。解決策としては、2つあると思う。
 1.1つ前のCb, Crの値を残しておいて、現在の値との平均を取る。
 2.気にせずに、現在のRGB - YCbCr変換のCbかCrの値を使う。

・色差信号はそんなに急激に変化しないと思うので、どちらでも大丈夫だと思うが、よりなだらかに色が変化するはずの1.の平均を取ることにしようと思う。通常のRGB4:4:4 - YCbCr4:2:2変換はどうやっているのだろうか?

つまりこれって画像圧縮の一種で色差信号の帯域を圧縮しているということになるんだろう?平均を取るとローパスフィルタを通しているのと同じことになると思う。
  1. 2012年11月20日 05:32 |
  2. ZedBoard
  3. | トラックバック:1
  4. | コメント:0

RGB―YCbCr変換の検討3(切り捨てと0捨1入)

RGB―YCbCr変換の検討2(Verilog HDLで実装)”の続き。(タイトルを四捨五入から0捨1入に変更しました)

今回、今までやってきたのは計算時に256を掛け算して(つまり2進数で言うと8ビット左論理シフト)して、計算してから、結果として15ビット目から8ビット目までを取り出していた(256で割り算する。つまり2進数で言うと8ビット右論理シフト)。これはつまり7ビット目から0ビット目の小数部を切り捨てていたわけだ。
0捨1入も行うことができる。7ビット目を見て、1だったら15ビット目から8ビット目に+1を行えば、0捨1入となる。実は0捨1入は演算が1つ増えるため(+1の演算器が1つ増える)採用していないで切り捨てていた。切り捨てによる誤差は、0≦x<-1に収まり、0捨1入による誤差は、-0.5≦x<0.5に収まる。この様に切り捨ては四捨五入に比べて平均で0.5だけ値が低くなるといえると思う。今回は切り捨てと0捨1入によるリソース使用量の違いと動作周波数について、Zynq-7020デバイスについて見ていこうと思う。(実際の型番は、xc7z020-1clg484)

今回、0捨1入のRGB-YCbCr変換のVerilog HDLを下に示す。
(2012/11/26:Y の変域が16から235だったためVerilog HDLソースファイルを修正しました。下のインプリメント後の結果と合わなくなる場合があります)

// RGB - YCbCr変換
// Y = 0.257R + 0.504G + 0.098B + 16
// Cb = -0.148R - 0.291G + 0.439B + 128
// Cr = 0.439R - 0.368G - 0.071B + 128
// 但し、Yは255以上だったら飽和演算をして255に丸める。Cb, Crは16以下だったら16に、240以上だったら240に飽和演算を行う。
// 0捨1入バージョン

`default_nettype none

module conv_rgb2ycbcr (
    input    wire    [7:0]    red,
    input    wire    [7:0]    green,
    input    wire    [7:0]    blue,
    output    reg        [7:0]    y,
    output    reg        [7:0]    cb,
    output    reg        [7:0]    cr
);
    
    wire    [18:0]    y_lshift8;
    wire    [18:0]    cb_lshift8;
    wire    [18:0]    cr_lshift8;
    

    assign y_lshift8 = ({5'd0, red, 6'd0} + {10'd0, red, 1'd0}) +  ({4'd0, green, 7'd0} + {11'd0, green}) + ({7'd0, blue, 4'd0} + {8'd0, blue, 3'd0} + {11'd0, blue}) + 19'd4096;
    
    assign cb_lshift8 = 19'd0 - ({6'd0, red, 5'd0} + {9'd0, red, 2'd0} + {10'd0, red, 1'd0}) - ({5'd0, green, 6'd0} + {8'd0, green, 3'd0} + {10'd0, green, 1'd0}) + ({5'd0, blue, 6'd0} + {6'd0, blue, 5'd0} + {7'd0, blue, 4'd0}) + 19'd32768;
    
    assign cr_lshift8 = ({5'd0, red, 6'd0} + {6'd0, red, 5'd0} + {7'd0, red, 4'd0}) - ({5'd0, green, 6'd0} + {7'd0, green, 4'd0} + {8'd0, green, 3'd0} + {9'd0, green, 2'd0} + {10'd0, green, 1'd0}) - ({7'd0, blue, 4'd0} + {10'd0, blue , 1'd0}) + 19'd32768;
    
    always @* begin
        if (y_lshift8[18]==1'b1 || y_lshift8[17:8]<16) // マイナスまたは16以下なので16に丸める
            y <= 8'd16;
        else if (y_lshift8[17:8] > 235) // 235より大きければ235に丸める
            y <= 8'd235;
        else begin
            if (y_lshift8[7] == 1'b1) begin
                if (y_lshift8[15:8] == 8'd235)
                    y <= 8'd235;
                else
                    y <=  y_lshift8[15:8] + 8'd1;
            end else
                y <=  y_lshift8[15:8];
        end
    end
    
    always @* begin
        if (cb_lshift8[18]==1'b1 || cb_lshift8[17:8]<16) // マイナスまたは16以下なので16に丸める
            cb <= 8'd16;
        else if (cb_lshift8[17:8] > 240) // 240より大きければ240に丸める
            cb <= 8'd240;
        else begin
            if (cb_lshift8[7] == 1'b1) begin
                if (cb_lshift8[15:8] == 8'd240)
                    cb <= 8'd240;
                else
                    cb <=  cb_lshift8[15:8] + 8'd1;
            end else
                cb <=  cb_lshift8[15:8];
        end
    end
    
    always @* begin
        if (cr_lshift8[18]==1'b1 || cr_lshift8[17:8]<16) // マイナスまたは16以下なので16に丸める
            cr <= 8'd16;
        else if (cr_lshift8[17:8] > 240) // 240より大きければ240に丸める
            cr <= 8'd240;
        else begin
            if (cr_lshift8[7] == 1'b1) begin
                if (cr_lshift8[15:8] == 8'd240)
                    cr <= 8'd240;
                else
                    cr <=  cr_lshift8[15:8] + 8'd1;
            end else
                cr <=  cr_lshift8[15:8];
        end
    end
endmodule

`default_nettype wire


切り捨てと四捨五入の違いを示す。前回の切り捨てのタイミングを下図に示す。
Conv_RGB2YCbCr_2_121118.png

次に四捨五入のタイミングを下図に示す。
Conv_RGB2YCbCr_3_121119.png

両方の図は同じ、red = 120, green = 212, blue = 76 の位置にカーソルを置いてある。切り捨てに比べて、0捨1入はカーソルの前から cb の値が 82 になっているのがわかると思う。これは、カーソルの前でcb_lshif8 が 0x051b8 となって、7ビット目が1になって、cb の値が0捨1入で繰り上がっているからである。

切り捨てと0捨1入の違いがわかったところで、使用リソースを比較してみる。
前回の最後に示した切り捨ての時の使用リソースはSlice Registersを41個、Slice LUTsを240個使用していた。
今回の四捨五入は、Slice Registersを43個、Slice LUTsを262個使用していた。なお、どちらも、Slice Registersは、AND/OR logicsとしての使用だった。FPGA Editorを見ると、Slice Registersは6入力2出力あるLUTの出力を2つ出すためにスルーロジックとして使われているようだ。
2つを比較すると、0捨1入(43 + 262) / 切り捨て(41 + 240) x 100 = 108.5% となり、0捨1入の方が切り捨てより 8.5% のリソースを余計に使用していた。

次に、動作周波数を検証してみよう。
切り捨てのRGB-YCbCr変換回路をconv_rgb2ycbcr_round_down.v、0捨1入のRGB-YCbCr変換回路をconv_rgb2ycbcr_round_off.v として、それぞれに入力、出力両方にFFを入れたトップファイルを作製した。(conv_rgb2ycbcr_rd_top.v, conv_rgb2ycbcr_ro_top.v)
conv_rgb2ycbcr_rd_top.v を下に示す。

// conv_rgb2ycbcr_top.v

`default_nettype none

module conv_rgb2ycbcr_rd_top (
    input    wire    clk,
    input    wire    reset,
    input    wire    [7:0]    red,
    input    wire    [7:0]    green,
    input    wire    [7:0]    blue,
    output    reg        [7:0]    y,
    output    reg        [7:0]    cb,
    output    reg        [7:0]    cr
);

    reg        [7:0]    red_ff, green_ff, blue_ff;
    wire    [7:0]    y_node, cb_node, cr_node;
    
    always @(posedge clk) begin
        if (reset) begin
            red_ff        <= 0;
            green_ff    <= 0;
            blue_ff        <= 0;
        end else begin
            red_ff        <= red;
            green_ff    <= green;
            blue_ff        <= blue;
        end
    end
    
    conv_rgb2ycbcr_round_down conv_rgb2ycbcr_rd_inst (
        .red(red_ff),
        .green(green_ff),
        .blue(blue_ff),
        .y(y_node),
        .cb(cb_node),
        .cr(cr_node)
    );
    
    always @(posedge clk) begin
        if (reset) begin
            y    <= 0;
            cb    <= 0;
            cr    <= 0;
        end else begin
            y     <= y_node;
            cb    <= cb_node;
            cr    <= cr_node;
        end
    end
endmodule

`default_nettype wire


これで制約に、クロック周波数を148.5MHzにして、インプリメントを行った。
切り捨ての方のタイミング制約は満たされずに、クリティカル・パスは 10.237ns となった。最大の動作周波数は、97.684MHzということになる。
Conv_RGB2YCbCr_4_121119.png

0捨1入の方のタイミング制約も満たされずに、クリティカル・パスは 10.565ns となった。最大の動作周波数は94.652MHz ということになる。
Conv_RGB2YCbCr_5_121119.png

案外、切り捨てと0捨1入の遅延時間の差が少ない。クリティカル・パスの経路を調べてみることにした。
上のProject Navigator の右側のウインドウのConstraint の "clk" 148.5MHz HIGH 50% のリンクをクリックする。
クリティカル・パスにカーソルを持って行って、右クリックメニューからshow in Technology Viewer を選択する。
Conv_RGB2YCbCr_6_121119.png

Technology Viewer が見えて、切り捨てでは9個のブロックがFFからFFまでで使用されていることがわかる。
Conv_RGB2YCbCr_7_121119.png

同様に、四捨五入も見てみると、0捨1入でも9個のブロックが使用されていることがわかった。
Conv_RGB2YCbCr_8_121119.png

1つ加算器が増えても、クリティカル・パスで使用するスライス数は同じなので、使用リソースは多少増えるが、値が確からしい0捨1入を使用することにした。
今回はLUTがどのようにコンフィグされているかまで、踏み込んで調べなかったが、+1の演算器だと足される数?が定数なので、簡単化されているのだと思う。
  1. 2012年11月19日 05:31 |
  2. 画像処理
  3. | トラックバック:0
  4. | コメント:0

RGB―YCbCr変換の検討2(Verilog HDLで実装)

”RGB―YCbCr変換の検討1(変換式)”の続き。

前回は変換式をVerilog HDLで記述できる形に変換した。今回は、実際にVerilog-HDLでRGB-YCbCr変換を記述した。
まず、RGB-YCbCr変換を行うconv_rgb2ycbcr.v を下に示す。
(2012/11/27:conv_rgb2ycbcr.v を修正しました。以下のインプリメントの回路規模とかが違っている場合があります)

// RGB - YCbCr変換
// Y = 0.257R + 0.504G + 0.098B + 16
// Cb = -0.148R - 0.291G + 0.439B + 128
// Cr = 0.439R - 0.368G - 0.071B + 128
// 但し、Yは255以上だったら飽和演算をして255に丸める。Cb, Crは16以下だったら16に、240以上だったら240に飽和演算を行う。
// 切り捨てバージョン

`default_nettype none

module conv_rgb2ycbcr_round_down (
    input    wire    [7:0]    red,
    input    wire    [7:0]    green,
    input    wire    [7:0]    blue,
    output    reg        [7:0]    y,
    output    reg        [7:0]    cb,
    output    reg        [7:0]    cr
);
    
    wire    [18:0]    y_lshift8;
    wire    [18:0]    cb_lshift8;
    wire    [18:0]    cr_lshift8;
    

    assign y_lshift8 = ({5'd0, red, 6'd0} + {10'd0, red, 1'd0}) +  ({4'd0, green, 7'd0} + {11'd0, green}) + ({7'd0, blue, 4'd0} + {8'd0, blue, 3'd0} + {11'd0, blue}) + 19'd4096;
    
    assign cb_lshift8 = 19'd0 - ({6'd0, red, 5'd0} + {9'd0, red, 2'd0} + {10'd0, red, 1'd0}) - ({5'd0, green, 6'd0} + {8'd0, green, 3'd0} + {10'd0, green, 1'd0}) + ({5'd0, blue, 6'd0} + {6'd0, blue, 5'd0} + {7'd0, blue, 4'd0}) + 19'd32768;
    
    assign cr_lshift8 = ({5'd0, red, 6'd0} + {6'd0, red, 5'd0} + {7'd0, red, 4'd0}) - ({5'd0, green, 6'd0} + {7'd0, green, 4'd0} + {8'd0, green, 3'd0} + {9'd0, green, 2'd0} + {10'd0, green, 1'd0}) - ({7'd0, blue, 4'd0} + {10'd0, blue , 1'd0}) + 19'd32768;
    
    always @* begin
        if (y_lshift8[18] == 1'b1 || y_lshift8[17:8]<16) // マイナスまたは16より小さいので16に丸める
            y <= 8'd16;
        else if (y_lshift8[17:8] > 235) // 235より大きければ235に丸める
            y <= 8'd235;
        else
            y <=  y_lshift8[15:8];
    end
    
    always @* begin
        if (cb_lshift8[18] == 1'b1 || cb_lshift8[17:8]<16) // マイナスまたは16より小さいので16に丸める
            cb <= 8'd16;
        else if (cb_lshift8[17:8] > 240) // 240より大きければ240に丸める
            cb <= 8'd240;
        else
            cb <=  cb_lshift8[15:8];
    end
    
    always @* begin
        if (cr_lshift8[18] == 1'b1 || cr_lshift8[17:8]<16) // マイナスまたは16より小さいので16に丸める
            cr <= 8'd16;
        else if (cr_lshift8[17:8] > 240) // 240より大きければ240に丸める
            cr <= 8'd240;
        else
            cr <=  cr_lshift8[15:8];
    end
endmodule

`default_nettype wire


次に、ISEのプロジェクトを作成して、conv_rgb2ycbcr.v をテストするテストベンチファイルconb_rgb2ycbcr.v を作製した。conb_rgb2ycbcr.v を下に示す。

`timescale 1ns / 1ps

////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer:
//
// Create Date:   04:57:17 11/18/2012
// Design Name:   conv_rgb2ycbcr
// Module Name:   K:/HDL/FndtnISEWork/Zynq-7000/ZedBoard/test/conv_rgb2ycbcr/conv_rgb2ycbcr_tb.v
// Project Name:  conv_rgb2ycbcr
// Target Device:  
// Tool versions:  
// Description: 
//
// Verilog Test Fixture created by ISE for module: conv_rgb2ycbcr
//
// Dependencies:
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
////////////////////////////////////////////////////////////////////////////////

module conv_rgb2ycbcr_tb;

    // Inputs
    reg [7:0] red;
    reg [7:0] green;
    reg [7:0] blue;

    // Outputs
    wire [7:0] y;
    wire [7:0] cb;
    wire [7:0] cr;
    
    integer i = 0;
    integer j = 0;
    integer k = 0;

    // Instantiate the Unit Under Test (UUT)
    conv_rgb2ycbcr uut (
        .red(red), 
        .green(green), 
        .blue(blue), 
        .y(y), 
        .cb(cb), 
        .cr(cr)
    );

    initial begin
        // Initialize Inputs
        red = 0;
        green = 0;
        blue = 0;

        // Wait 100 ns for global reset to finish
        #100;
        
        // Add stimulus here
        for (i=0; i < 100; i = i + 10) begin
            red = i;
            green = i;
            blue = i;    // 色は白
            #10;
        end
        
        for (i=0; i<256; i=i+1) begin
            for (j=0; j<256; j=j+1) begin
                for (k=0; k<256; k=k+1) begin
                    red = i;
                    green = j;
                    blue = k;
                    #10;
                end
            end
        end
        
        $finish;
    end
      
endmodule


最初は、RGBとも同じ値をシミュレーションしている。つまり白色をシミュレーションしているわけだ。
白色の場合はYの値は変わるが、Cb, Crの値は128で固定されている。なお、RGBがオール0の場合はYは16となる。下に白色の場合のシミュレーション結果を示す。
Conv_RGB2YCbCr_1_121118.png

シミュレーション結果の一部を下に示す。
Conv_RGB2YCbCr_2_121118.png

(追加)試しにインプリメントしてみました。MAPのリポートの一部を下に示します。

Release 14.3 Map P.40xd (nt)
Xilinx Mapping Report File for Design 'conv_rgb2ycbcr'

Design Information
------------------
Command Line   : map -intstyle ise -p xc7z020-clg484-1 -w -logic_opt off -ol
high -t 1 -xt 0 -register_duplication off -r 4 -mt off -ir off -pr off -lc off
-power off -o conv_rgb2ycbcr_map.ncd conv_rgb2ycbcr.ngd conv_rgb2ycbcr.pcf 
Target Device  : xc7z020
Target Package : clg484
Target Speed   : -1
Mapper Version : zynq -- $Revision: 1.55 $
Mapped Date    : SUN 18 NOV 6:1:11 2012

Design Summary
--------------
Number of errors:      0
Number of warnings:   50
Slice Logic Utilization:
  Number of Slice Registers:                    41 out of 106,400    1%
    Number used as Flip Flops:                   0
    Number used as Latches:                      0
    Number used as Latch-thrus:                  0
    Number used as AND/OR logics:               41
  Number of Slice LUTs:                        240 out of  53,200    1%
    Number used as logic:                      238 out of  53,200    1%
      Number using O6 output only:             117
      Number using O5 output only:               8
      Number using O5 and O6:                  113
      Number used as ROM:                        0
    Number used as Memory:                       0 out of  17,400    0%
    Number used exclusively as route-thrus:      2
      Number with same-slice register load:      0
      Number with same-slice carry load:         2
      Number with other load:                    0

Slice Logic Distribution:
  Number of occupied Slices:                    93 out of  13,300    1%
  Number of LUT Flip Flop pairs used:          240
    Number with an unused Flip Flop:           199 out of     240   82%
    Number with an unused LUT:                   0 out of     240    0%
    Number of fully used LUT-FF pairs:          41 out of     240   17%
    Number of slice register sites lost
      to control set restrictions:               0 out of 106,400    0%

  1. 2012年11月18日 05:57 |
  2. 画像処理
  3. | トラックバック:0
  4. | コメント:0

RGB―YCbCr変換の検討1(変換式)

RGBで表されたピクセルをADV7511に表示させるためにはYCbCrに返還する必要がある。その変換回路を実現するためにRGB-YCbCr変換回路を設計することにした。先ずは変換式を検討してみた。

YUV-RGB変換1(方式の検討)”を式変換の参考にしながら、”YUVフォーマット及び YUV<->RGB変換”ページの”ITU-R BT.601 規定YCbCrと8bitフルスケールRGBの相互変換”を参考にさせて頂いた。
そのページによるとのRGB-YCbCrの変換式は下のようになる。

Y = 0.257R + 0.504G + 0.098B + 16
Cb = -0.148R - 0.291G + 0.439B + 128
Cr = 0.439R - 0.368G - 0.071B + 128
但し、Yは255以上だったら飽和演算をして255に丸める。Cb, Crは16以下だったら16に、240以上だったら240に飽和演算を行う。


これを式1とする。

式1の右辺に256を掛けて256で割ることにする。256は8ビットのシフト演算で行うことができる。
電卓の関係上10進数で計算する。最初のRの係数0.257に256を掛けると65.792になった。小数点以下を四捨五入すると、66になる。この様に係数に256を掛けて四捨五入を行うと式2が導けた。

Y = (66R + 129G + 25B + 4096) >> 8
Cb = (-38R - 74G + 112B + 32768) >> 8
Cr = (112R - 94G - 18B + 32768) >> 8


これを式2とする。>>8は8ビット右シフトするということで、これは256で割ったのと等価になる。
32768は0x8000_0000で、16ビット長で、最大値になる項は、129Gだから、最大値はGを255とした時で、129x255 = 32895 = 0x807F でやはり16ビットになる。演算器のビット幅は、16bit + (log2(項の数 = 4)の小数点以下切り上げ) + 符号ビット1bit = 19bit とする。

式2を更に2進数に変換する。

Y = (0100_0010R + 1000_0001G + 0001_1001B + 19'd4096) >> 8
Cb = (-0010_0110R - 0100_1010G + 0111_0000B + 19'd32768) >> 8
Cr = (0111_0000R - 0101_1110G - 0001_0010B + 19'd32768) >> 8


これを式3とする。

これで、Verilog HDL の式にできる形になった。これからVerilogでRGB-YCbCr変換回路を作る予定だ。
  1. 2012年11月17日 05:39 |
  2. 画像処理
  3. | トラックバック:0
  4. | コメント:0

ET2012に行って来ました。

昨日はET2012に行って来ました。主にFPGA関連の展示やカンファレンスを聞いてきて、TwitterのTLの方に4人ほどお会いできました。充実した1日でした。お会いできました皆様、説明していただいた皆様有難うございました。会場は混んでいてとっても活気がありました。日本の組み込み、まだまだ元気だと思います。。。

セミナは、展示を見るのが忙しかったので、”FPGAを凌駕するハイパフォーマンス3PLDデバイスと応用例の紹介”と”これからのマルチコア・プロセッサ・システム~ ARMプロセッサ、Nios II そしてOpenCL~”FPGAトラックの2つの講演を聞いてきました。
最初のは、Tabula社の3D-PLDの話でした。IntelをFabとして使って、22nmプロセスでFPGAを作っていると話題になったあの会社です。基本のクロックは166MHzなんですが、それを12倍して2GHzで動かして、基本クロックの間に12個の違った回路を2GHzクロックごとにコンフィグして12倍の回路として動作させるというものです。ホントだったら凄いですね。後でブースにも言って説明を聞いてきたんですが、LUTの後にFF(FPGAではお決まりの回路です。普通のFPGAだったらここのFFは使わないこともありますが)Spacetimeアーキテクチャでは必須で使うとのことです。2GHzで動くのは凄いなと思いましたが、LUT1個当たり2GHzのようでした。少なくとも長い配線を通しては2GHzで動かない様です。そうなるとツールの重要性が普通のFPGAより増すだろうと思いました。ツールのでき次第で評価がかわるんじゃないか?と思います。
2つ目のこれからのマルチコア・プロセッサ・システムはAltera社の講演でした。28nm FPGAの説明やAltera のSoC FPGA、そしてOpenCLの説明でした。SoC FPGAではZynqとの違いを説明していました。
・128ビット幅でARMとFPGAがやり取りできるようです。(Xilinxは64ビット幅)
・DDRメモリのハードマクロのコントローラがFPGA側にも付くようです。
・FPGA単体でもコンフィグROMでコンフィグできるようです。(Xilinxはプロセッサ経由コンフィギュレーションのみ)
OpenCLはPCIe経由でパソコンとFPGAボード間をやり取りするみたいです。ブースに行って確認したところ動いていましたが、アルファバージョンの様な感じでした。

ブースには、アキュベリノスHARCAがありました。なんかどこかで見たようなボードです。これは、ブレッドボードに挿して周辺を作ってもらう学生実験にぴったりなボードだと思います。この基板を使って、aitendoのOV9655を付けたカメラボードも展示してありました。エネループ乾電池2本で動いていました。
XilinxのブースではZynqがいっぱい展示されていました。TEDの4K2Kテレビ用のHDビデオを入れて、4K2Kにするスケーラーを展示していました。構成は、TEDのKintex7のボードHDMIの入力ボード、出力ボードX2を付けて実現していました。ディスプレイは4K2Kを表示するのに、HDディスプレイを4枚使って表示していました(マウンタ使用)。4k2Kディスプレイは高いので、お安く済ますのにはとっても良いと思いました。
特電のブースにも行って、なひたふさんにスマートフォンからJTAGを使うデモを見せて頂きました。端子の情報が見えるだけでなく、スマートフォンの画面に表示されたICチップの端子を触ると遠隔で端子のレベルを変更することが出来ます。

ETフェスタまでいて、Twitterでお世話になっている方にお会いして来ました。Aさん、おいしいパンをありがとうございました。Gさん、マドレーヌ(ですよね?)美味しかったです。ありがとうございました。
1日会場にいて疲れましたが、色々見られて楽しかったです。

追加です。大学のブースで面白いものを見ました。Spartan-6のちっちゃなボードが壮観につながっていました。A Scalable Many-core Processor Simulatorだそうです。コネクタでFPGAボードが平面につながっていて、メニイコアをシミュレーションできるシステムみたいですね。面白そうでした。東工大の吉瀬先生のところのプロジェクトのようです。
  1. 2012年11月16日 05:29 |
  2. その他のFPGAの話題
  3. | トラックバック:0
  4. | コメント:0

ZedBoardでHDMI出力9(ADV7511のレジスタ設定3)

何か、飽きてきたのですが、途中でやめられないので続けます。

0xd0番地 -> 0x3c
[7] -> Enable DDR Negative Edge CLK Delay => 0 = Disable DDR Negative Edge CLK Delay , 1 = Enable DDR Negative Edge CLK Delay
[6:4] -> DDR Negative Edge CLK Delay => 000 = -1200 ps , 001 = -800 ps , 010 = -400 ps , 011 = no delay , 100 = 400 ps , 101 = 800 ps , 110 = 1200 ps , 111 = Invert CLK
[3:2] -> Sync Pulse Select 00 = DE , 01 = Hsync , 10 = Vsync , 11 = no sync pulse
[1] -> Timing Generation Sequence => 0 = sync adjustment then DE generation , 1 = DE generation then sync adjustment
[0] -> Fixed => Must be set to Default Value

0xd1番地 -> 0xff
[7:0] -> Fixed => Must be set to Default Value

0xde番地 -> 0x9c
[7:4] -> Fixed => Must be set to Default Value (1001)
[3] -> TMDS Clock Inversion 0 = Normal TMDS Clock , 1 = Inverted TMDS Clock
[2:0] -> Fixed => Must be set to Default Value (100)

0xe0番地 -> 0xd0
[7:0] -> Fixed => Must be set to 0xD0 for proper operation

0xe4番地 -> 0x60
[7:0] -> Fixed => Must be set to Default Value

0xf9番地 -> 0x00
[7:0] -> Fixed I2C Address => This should be set to a non-conflicting I2C address (0x00)

0xfa番地 -> 0x00
[7:5] -> Hsync Placement MSB (Embedded Sync Decoding) => 000
[4:2] -> Hsync Placement MSB (Sync Adjustment) => (000)

0x17番地 -> 0x02
[7] -> Fixed => Must be set to Default Value (0)
[6] -> Vsync Polarity => (Case 2:Sync Adjustment Register (0x41[1]) = 0) 0 = sync polarity pass through, High polarity , 1 = sync polarity invert, Low polarity
[5] -> Hsync Polarity => (Case 2:Sync Adjustment Register (0x41[1]) = 0) 0 = sync polarity pass through, High polarity , 1 = sync polarity invert, Low polarity
[4:3] -> Reserved @ 00b => Must be set to Default Value
[2] -> 4:2:2 to 4:4:4 Interpolation Style => 0 = use zero order interpolation , 1 = use first order interpolation
[1] -> Aspect Ratio => 0 = 4:3 Aspect Ratio , 1 = 16:9 Aspect Ratio
[0] -> DE Generator Enable => 0 = Disabled , 1 = Enabled
  1. 2012年11月15日 04:43 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

ZedBoardでHDMI出力8(ADV7511のレジスタ設定2)

0x55番地 -> 0x20
[7] -> AVI Byte 1 bit 7 => Reserved per HDMI spec. - set to 0
[6:5] -> Y1Y0 (AVI InfoFrame) => 00 = RGB , 01 = YCbCr 4:2:2 , 10 = YCbCr 4:4:4 , 11 = reserved
[4] -> Active Format Information Status (AVI InfoFrame) => 0 = no data , 1 = Active format Information valid
[3:2] -> Bar Information (AVI InfoFrame) => 00 = invalid bar , 01 = vertical , 10 = horizontal , 11 = Both
[1:0] -> Scan Information (AVI InfoFrame) => 00 = no data , 01 = TV , 10 = PC , 11 = None

0x56番地 -> 0x08
[7:6] -> Colorimetry (AVI InfoFrame) => 00 = no data , 01 = ITU601 , 10 = ITU709 , 11 = Extended Colorimetry Information Valid (Indicated in register 0x57[6:4])
[5:4] -> Picture Aspect Ratio (AVI InfoFrame) => 00 = no data , 01 = 4:3 , 10 = 16:9 , 11 = None
[3:0] -> Active Format Aspect Ratio (AVI InfoFrame) => 1000 = Same as Aspect Ratio , 1001 = 4:3 (center) , 1010 = 16:9 (center) , 1011 = 14:9 (center)

0x96番地 -> -0x20
[7] -> HPD Interrupt => 0 = no interrupt detected , 1 = interrupt detected
[6] -> Monitor Sense Interrupt => => 0 = no interrupt detected , 1 = interrupt detected
[5] -> Vsync Interrupt => 0 = no interrupt detected , 1 = interrupt detected
[4] -> Audio FIFO Full Interrupt => => 0 = no interrupt detected , 1 = interrupt detected
[3] -> Fixed => 0
[2] -> EDID Ready Interrupt => => 0 = no interrupt detected , 1 = interrupt detected
[1] -> HDCP Authenticated => => 0 = no interrupt detected , 1 = interrupt detected
[0] -> Fixed @ 0b => Reserved

0x98番地 -> 0x03
[7:0] -> Fixed => Must be set to 0x03 for proper operation

0x99番地 -> 0x02
[7:0] -> Fixed => Must be set to Default Value (0x02)

0x9a番地 -> 0xe0
[7:0] -> Fixed => Must be set to 0b1110000 for proper operation

0x9c番地 -> 0x30
[7:0] -> Fixed => Must be set to 0x30 for proper operation

0x9d番地 -> 0x61
[7:4] -> Fixed => Must be set to Default Value (0110)
[3:2] -> Input Pixel Clock Divide => 00 = Input Clock not Divided , 01 = Input Clock Divided by 2 , 10 = Input Clock Divided by 4 , 11 = Invalid Setting
[1:0] -> Fixed => Must be set to 0b01 for proper operation

0xa2番地 -> 0xa4
[7:0] -> Fixed => Must be set to 0xA4 for proper operation

0xa3番地 -> 0xa4
[7:0] -> Fixed => Must be set to 0xA4 for proper operation

0xa5番地 -> 0x44
[7:1] -> Fixed => Must be set to Default Value (Default Value = 0000 010*) レジスタの説明と矛盾している、なぜだろうか?

0xab番地 -> 0x40
[7:3] -> Fixed => Must be set to Default Value (01000***)

0xaf番地 -> 0x06
[7] -> HDCP Enable => 0 = HDCP Disabled , 1 = HDCP Encryption Enabled
[6:5] -> Fixed => Must be set to Default Value
[4] -> Enable HDCP Frame Encryption => 0 = Current Frame NOT HDCP Encrypted , 1 = Current Frame HDCP Encrypted
[3:2] -> Fixed => Must be set to Default Value (01)
[1] -> HDMI/DVI Mode Select => 0 = DVI Mode , 1 = HDMI Mode
[0] -> Fixed => Must be set to Default Value (0)

0xba番地 -> 0x00
[7:5] -> Clock Delay => 000 = -1.2ns , 001 = -0.8ns , 010 = -0.4ns , 011 = no delay , 100 = 0.4ns , 101 = 0.8ns , 110 = 1.2ns , 111 = 1.6ns
[4] -> Internal/External HDCP EEPROM => 0 = external EEPROM , 1 = internal EEPROM
[3] -> Fixed => Must be set to Default Value
[2] -> Display AKSV => 0 = Don't Show AKSV , 1 = Show AKSV in 0xB0 - 0xB4
[1] -> Ri Two Point Check => 0 = HDCP Ri standard , 1 = enable HDCP Ri two point check

  1. 2012年11月14日 05:35 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

ZedBoardでHDMI出力7(ADV7511のレジスタ設定1)

ANALOG DEVICESリファレンス・デザインにおける画像の主要なADV7511のレジスタ設定を見ていきます。

レジスタの設定1(リファレンス・デザインでの設定)
0x15番地 -> 0x01
[3:0] -> Input ID => 0000 = 24 bit RGB 4:4:4 or YCbCr 4:4:4 (separate syncs) , 0001 = 16, 20, 24 bit YCbCr 4:2:2 (separate syncs) ...

0x16番地 -> 0xb5
[7] -> Output Format => 0 = 4:4:4 , 1 = 4:2:2
[5:4] -> Color Depth => 00 = invalid , 10 = 12bit , 01 = 10bit , 11 = 8bit
[3:2] -> Input Style => 00 = Not Valid , 01 = style 2 , 10 = style 1 , 11 = style 3 (”ZedBoardでHDMI出力13(HDMIに出力できた)”参照)
[1] -> DDR Input Edge => 0 = falling edge , 1 = rising edge
[0] -> Output Colorspace for Black Image => 0 = RGB , 1 = YCbCr

0x18番地 -> 0x46
[7] -> CSC Enable => 0 = CSC Disabled , 1 = CSC Enabled
[6:5] -> CSC Scaling Factor => 00 = +/- 1.0, -4096 - 4095 , 01 = +/- 2.0, -8192 - 8190 , 10 = +/- 4.0, -16384 - 16380 , 11 = +/- 4.0, -16384 - 16380

0x40番地 -> 0x80
[7] -> GC Packet Enable => 0 = GC Packet Disabled , 1 = GC Packet Enabled
[6] -> SPD Packet Enabled => 0 = Disabled , 1 = Enabled
[5] -> MPEG Packet Enabled => 0 = Disabled , 1 = Enabled
[4] -> ACP Packet Enable => 0 = Disabled , 1 = Enabled
[3] -> ISRC Packet Enable => 0 = Disabled , 1 = Enabled
[2] -> GM Packet Enable => 0 = Disabled , 1 = Enabled
[1] -> Spare Packet 2 Enable => 0 = Disabled , 1 = Enabled
[0] -> Spare Packet 1 Enable => 0 = Disabled , 1 = Enabled

0x41番地 -> 0x10
[6] -> POWER DOWN => 0 = Normal Operation , 1 = ADV7511 Powered Down
[5] -> Fixed => 0
[4] -> Reserved @ 1b => 1
[3:2] -> Fixed => 00
[1] -> Sync Adjustment Enable => 0 = Disabled , 1 = Enabled
[0] -> Fixed => 0

0x48番地 -> 0x08
[7] -> Reserved => 0
[6] -> Video Input Bus Reverse => 0 = Normal Bus Order , 1 = LSB .... MSB Reverse Bus Order
[5] -> DDR Alignment => 0 = DDR input is D[17:0] , 1 = DDR input is D[35:18]
[4:3] -> Bit Justfication for YCbCr 4:2:2 modes => 00 = evenly distributed , 01 = right justified , 10 = left justified , 11 = Invalid

0x49番地 -> 0xa8
[7:2] -> Bit Trimming Mode => 000000 = Active Dither , 101010 = Truncate

0x4C番地 -> 0x00 (Read Only)
[7:4] -> Pixel Packing (GC Packet)
[3:0] -> Color Depth (GC Packet) => 0000 = Color Depth Not Indicated, 0100 = 24 Bits/Pixel , 0101 = 30 Bits/Pixel , 0110 = 36 Bits/Pixel
  1. 2012年11月13日 05:56 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:2

ZedBoardでHDMI出力6(ADV7511の勉強)

ADV7511の機能について探っていく覚書

・Display Data Channel は、I2C Master が載っていて、EDIDをダウンロードしてバッファリングするそうだ。

・サポートフォーマット
  ・Color Space   YCbCr or RGB
  ・Bits per Color 8, 10, or 12
  ・Format     4:2:2 or 4:4:4
  ・Sync Type   Speparate or Embedded
  ・Clocks     1x, 2x or DDR

・Video Data input は8, 10, 12, 16, 20, 24, 30, 36ビット幅があるが、ZedBoardはD23~D8の16ビット幅

・ADV7511のSDA/SCLのメイン・レジスタ・マップのプログラム・アドレスは0x72と0x7Aで、PD/AD端子を10KΩでプルアップした時が0x7A、プルダウンまたGNDに接続した時が0x72

・ZedBoardではPD/AD端子が、GNDに接続されているので、アドレスは0x72となる。

・I2Cの動作周波数は up to 400KHzだそうだ。

・ZedBoardでのVido Input Table はID=1 Bits per Color 8bit, Maximum Input Clock 165.0MHz, Format Name YCbCr 4:2:2, Sync Type Separate Syncs (ADV7511 Programming Guide Rev. G 27ページ)

・Table 18 YCbCr 4:2:2 Formats (24, 20, or 16 bits) Input Data Mapping:
 0x48[4:3] = ‘01’ (right justified) Input ID = 1 の16bit(Input ID = 1: An input with YCbCr 4:2:2 with separate syncs)( Input ID (0x15[3:0]) to 0b0001)
 
 > 16bit 1st Y[7:0], Cb[7:0], 2nd Y[7:0], Cr[7:0]

  1. 2012年11月12日 04:47 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

ZedBoardでHDMI出力5(BDC の作製1)

HDMI出力とVGA出力を同時に出来るBitmap Display Controller (BDC) を作ることにした。今までのBDC は、VGA出力だけだったが、それにHDMI出力を追加する。

当初は、VDMAやIICコントローラなどの既存のIPを使おうと思ったが、IPのマニュアルを読んだり、構造を勉強したりするのが、結構大変なので、自分のビットマップ・ディスプレイ・コントローラを修正することにした。

HDMI出力を行うためにBDC の内部構造を変更する。そのブロック図を図1に示す。
ZedBoard_HDMI_26_121111.png
図1 Bitmap Display Controller (BDC) ブロック図

Zynq PS HP0ポートからAXI3バスでAXI Interconnect に送られたPixelデータは、AXI4バスでBDC に送られる。そのPixelデータはPixel DMA Controller がMaster としてAXI Interconnect にリクエストを送って取得する。Pixel DMA Controller から出力されたRGB のPixel Data はDisplay Timing Controller に送られる。ここでPixel Dataの取得タイミングとHSYNC, VSYNCなどの表示タイミングを出力する。表示タイミングに揃えたRGBのPixel Data をVGA Controller とHDMI Controller に送る。VGA Controller は従来通りに各4ビットのRGBデータをDACに送る。HDMI Controller はADV7511 (Low-Power HDMI® Transmitter) にYCrCbデータを送る。

HDMI Controller のブロック図を図2に示す。
ZedBoard_HDMI_27_121111.png
図2 HDMI Controlle (HDMIC) ブロック図

I2C Controller を置いて初期化の時に、ADV7511のレジスタに設置値をセットする。Register Configuration File にADV7511 のレジスタの設定値を保存しおく。RGB Pixel DataはRGB to YCbCr Converter でRGBからYCbCr4:2:2 に変換される。変換されたYCbCr4:2:2 データはADV 7511 Display Controller で制御されてADV75111 に送られる。

とりあえずは、この様な回路を構成することにした。なお、ADV7511のデータシートは完全なデータシートが見つからなかったが、ツイッターで教えて頂いた。その他、Programming Guide なども見つけたので、下にURLを示す。
ADV7511 Design Support Files

2012/11/12 修正:以前の上のリンクはADV7511Wという別のHDMI Trasmitter でした。ADV7511のファイルのリンクに修正しました。しかし、何でこんなにわかりにくいところにあるんでしょうか?探すのにとっても苦労しました。)
  1. 2012年11月11日 05:55 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

Linuxでのフレームバッファの使い方、デバイスドライバについて

Linuxでのフレームバッファの使い方、デバイスドライバについて、昨日ツイッターで色々教えてもらったことをtogetter でまとめました。

たぶん、フレームバッファのドライバを使うところまでは必要ないと思うんですが、勉強しておきたいと思います。
  1. 2012年11月09日 03:48 |
  2. Linux
  3. | トラックバック:0
  4. | コメント:0

ZedBoardでHDMI出力4(ChipScope AXI Monitor でAXIバスを観察3)

ZedBoardでHDMI出力3(ChipScope AXI Monitor でAXIバスを観察2)”の続き。

前回はHPポートのVDMAに接続されたAXIバスの様子をChipScopeで見てみたが、今回はVDMAからaxi_hdmi_tx_16b_0 につながっているAXI-streamバスのトランザクションを見ていこう。

Xilinx社のFPGAにおけるAXI-Streamバスの信号はAXIリファレンスガイドに書いてある。このマニュアルの50ページの表 3-2 : AXI4-Stream の信号 を引用させていただく。
ZedBoard_HDMI_23_121108.png

これによると、AXI-StreamはTVALIDだけ必須で後はオプションとのことだ。TKEEPは未使用と書いてあるが、ChipScopeの信号には入っていた。TDATAは必ず8ビットの倍数にするそうだ。

さて、AXI-Streamバスの信号をChipScpeで見てみよう。
ZedBoard_HDMI_24_121108.png

TVALIDはいつも1で、時々TREADYをaxi_hdmi_tx_16b_0 が1にアサートしてデータを受け取っている。TLASTもVDMAによって、たまに1にアサートされている。

TLASTが1になった時には、どういう意味があるかを調べてみた。
LogiCORE IP AXI Video Direct Memory Access v5.02.a Product Guide PG020 July 25, 2012によると、TLASTはそれぞれのラインの終了を表すそうだ。Example MM2S Timing、111ページ参照。
Example MM2S Timing、111ページの Figure 3-10: Example MM2S Interface Timing を下に引用させていただく。
ZedBoard_HDMI_25_121108.png

このExample Timing とChipScope のタイミングを見ると違っているところがある。Example Timing では、TREADYが常時1にアサートされているが、ChipScopeではTVALIDが常時1にアサートされていて、TREADYが時々1になってデータを受け取っている。

AXI-Stream はデータ・ストリームをやり取りするだけなので、簡単なバス構造になっているようだ。
  1. 2012年11月08日 05:21 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

ZedBoardでHDMI出力3(ChipScope AXI Monitor でAXIバスを観察2)

ZedBoardでHDMI出力2(ChipScope AXI Monitor でAXIバスを観察1)”の続き。

前回は、ChipScope Integrated Controller を1つ、ChipScope AXI Monitor をHPバス用と、VDMAのAXI Stream 用に2つ追加追加して、XPSでインプリメントが終了し、SDKでコンパイルが終了した。
cf_adv_7511_zedの右クリックメニューでRun as -> Run Configurations... を選択し、cf_adv_7511_zed Debugコンフィグレーションを作成して、Runしたら、”ZedBoardでHDMI出力1(アナデバのリファレンス・デザイン)”同様のサンプル画像と音が出てきた。
ZedBoard_HDMI_14_121029.jpg

更に、今回はXPSプロジェクトなので、スタートメニューから、ISE14.1のChipScope Pro Analyzer を単独で起動した。
・Open Cabel/Sreach JTAG Chain ボタンをクリックして、Zynqを認識させた。

・信号名が入っていないDataportが表示されたので、ProjectウインドウのUNIT:0 の右クリックメニューからimport... を選択した。こうすると、各UNIT毎に.cdc ファイルを適用できる。
ZedBoard_HDMI_17_121107.png

・cf_adv7511_zed\cf_adv7511_zed\implementation\chipscope_axi_monitor_0_wrapper フォルダにあるchipscope_axi_monitor_0.cdc を指定した。

・ProjectウインドウのUNIT:1 の右クリックメニューからimport... を選択した。

・cf_adv7511_zed\cf_adv7511_zed\implementation\chipscope_axi_monitor_stream_wrapper フォルダにあるchipscope_axi_monitor_stream.cdc を指定した。

これで、ChipScopeの設定が終了した。Triger Immediate ボタンをクリックすると波形が現れた。
ZedBoard_HDMI_18_121107.png

VDMAのHP0ポートの波形を示す。
ZedBoard_HDMI_19_121107.png

最初のトランザクションの塊を引き伸ばして、タイミングを見た。RDATAは64ビット幅になっている。
ZedBoard_HDMI_20_121107.png

データを連続バースト転送している幅は84クロックだった。ARVALIDが1になるよりも前にRVALIDとRREADYが同時に1になっているのがわかるだろうか?これは以前のトランザクションが遅延して残っているようだ。84クロックはHP0ポートのバッファ量を示しているのではないか?と思う。それにしても、1クロックの遅延もなくデータのバースト転送が続くのは見事だと思う。スループットが最大になっている。processing_system_7_0::FCLK_CLK1 は200MHzだったので、64ビットデータ幅=8バイト、8バイト x 200MHz = 1.6GB/sec の転送スピードとなる。ビットに直せば12.8Gbit/sec だ。しかしどうして、連続アドレスにアクセスしていると思うのだが、バースト長を長くしないのだろう?

1番目と2番めにARVALIDが1になっているところを拡大してみた。
ZedBoard_HDMI_21_121107.png

最初のARVALIDが1のアドレスが 021E2500 で、次のアドレスが、021E2580 になっている。つまり0x80分のアドレスの差があることになる。つまり、1つのトランザクションで、128バイト転送していることになる。バースト数は128バイト / 8バイト(64ビットバス幅) = 16バーストとなる。
次に、ARVALIDが1になる時の各制御信号を見ていこう。信号の意味は、”AXI4バスのパラメータの意味”を参照のこと。

ARLEN = 0F : 16バースト転送
ARSIZE = 3 : 1回に転送するバイト数は8バイト
ARBURST = 1 : INCR(アドレスをインクリメント)
ARCACHE = 3 : Normal Non-cacheable Bufferable


ZedBoardにビットマップ・ディスプレイ・コントローラを追加する13(char_wirte_axi_master IPを追加4)”でchar_write_axi_master IP、つまりAXIバスのWrite マスタを追加した時には、ARCACHE = 3、つまりBufferableにすると、BRESPに”10”、SLVERR (Slave error) が帰ってきた。Writeではダメだが、ReadではARCACHEを 3 Bufferable にしても良いようだ。

(2013/10/21:追記)
ISE14.6を使用して、AWCACHE、ARCACHEの値を 3 にして、AXIバスのデータ転送を行ってみました。Write もRead もBRESP、RRESPの返り値が 0 で問題ないことが分かりました。”AXI4バスでのAWCACHE、ARCACHEの値”を参照下さい。


VDMAのRead Transaction Issuance Limit はAuto になっていたが、たぶんデフォルトでは8だと思うので、8個のアドレスまで発行できる。つまり今のデータ転送はどこのアドレスなのか、この部分を切り取っただけでは分からない。

VDMAは性能が良いと思った。これならば、使わせてもらって、自分の回路を修正しても良いかな?

最後にHP0ポート、最大性能1.6GB/sec の帯域のどのくらいを使用しているか?だが、この時点では、84 / 226 x 100 = 37% 使用している。
ZedBoard_HDMI_22_121107.png

表示空間はこのくらいのデータ帯域を使用していると思う。ブランク期間はデータ帯域を使用していないはずだ。

(追記:AXIマスタとしてReadしているVDMAがHP0ポートを待たせている(RREADY = 0)。これで問題ないのだろうか?PCI-Xの仕様ではRead要求したデータはWaitせずに受けること、となっていたと思う。AXIはバスとは言ってもクロスバーになので、他のデバイスのスループットやレイテンシに問題はないのだと思う)
  1. 2012年11月07日 05:08 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

ZedBoardでHDMI出力2(ChipScope AXI Monitor でAXIバスを観察1)

ZedBoardでHDMI出力1(アナデバのリファレンス・デザイン)”の続き。

前回はリファレンス・デザインをそのままやってみたが、HDMI経由で画像と音を出力することが出来た。音はまだしも、HDMI経由で画像を出したい。リファレンス・デザインそのままというのも良いかもしれないが、同時にVGAポートにも出力したい。そのために、axi_hdmi_tx_16b (HDMI IP) のHDLファイルを使わせて頂いて、独自IPに埋め込むということは、ADV7511のデータシートが全く無く、動作がわからない点から言ってもダメそうだ。その場合には、アナログ・デバイスのHDMI IPにVGA出力を組み込むことになる。また、AXI Stream Busがどのように動いているかを勉強してみたい。よって、XPSプロジェクト上の axi_vdma_0 のHP0バスと、axi_hdmi_tx_16b_0 とaxi_vdma_0 間のAXI Stream バスにChipScope AXI Monitor をつないで信号を観測してみることにした。
:ISE14.3 に変換するとSDKでエラーになってしまったので、ISE14.1で動作チェックをおこなった)

前回、”ChipScope AXI Monitor を試してみた”では、ChipScope AXI Monitor をAdd IPせずに、DebugメニューからDebug Configuration を選択して、ChipScope AXI Monitor を追加した。今回はAdd IPしてChipScope AXI Monitor IPコアを追加した。更に、ChipScope AXI Monitor を追加する際には、ChipScope Integrated Controller も必須だった。今回は、ChipScope Integrated Controller を1つ、ChipScope AXI Monitor をHPバス用と、VDMAのAXI Stream 用に2つ追加した。
下に追加したXPSプロジェクトの Bus Interfaces タブを示す。
ZedBoard_HDMI_14_121106.png

Ports タブでは、2つのChipScope AXI Monitor のMON_AXI_ACLK をprocessing_system_7_0::FCLK_CLK1に接続した。chipscope_icon_0 (controllerを2つに増やした)の2つのcontrol を2つのChipScope AXI Monitor に接続した。(processing_system_7_0::FCLK_CLK1 は200MHz)
ZedBoard_HDMI_15_121106.png

これで、Generate BitStream を行った。終了後、Export Design をSDKを立ち上げるようにボタンをクリックして、SDKにハードウェアをエクスポートした。

SDKが立ちあげったら、Xilinx C Project (cf_adv7511_zed) を新規作成して、リファレンス・デザインのcf_adv7511_zed\cf_adv7511_zed\sw フォルダにあるcf_adv7511_zed.h と cf_adv7511_zed.c をcf_adv7511_zed/src にドラッグアンドドロップした。

11行目の”#include "platform.h"”、151行目の”init_platform();”、249行目の”cleanup_platform();”はエラーが出るので、コメントアウトした。
ZedBoard_HDMI_16_121106.png

これで、コンパイルが通った。
  1. 2012年11月06日 05:49 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

アクリルサインの試作第1号が完成

アクリルサインの試作第1号が完成しました。
acrylic_signs_16_121105.jpg

横から見るとこんな感じです。5mm アクリル+10mm アクリル + 5mm アクリルのサンドイッチで20mm厚です。
acrylic_signs_17_121105.jpg

LEDを点灯するとこんな感じです。上にFPGA-CAFEのアクリルサインを載せてあります。
acrylic_signs_18_121105.jpg

写真やビデオでは、目で見ている色は出ません。MFT2012の会場で御覧ください。と言っても、明るすぎるとあまり派手じゃないかもしれません。

表板のUSBミニBコネクタへの切り込みが浅かったので、よく挿入できません。やっとはまっている感じです。後、LEDは目隠しをする予定なのを忘れていました。表板を掘り込んで白いアクリルをはめ込みます。
来週、表板、裏板を作りなおして修正します。
  1. 2012年11月05日 05:31 |
  2. Make出展
  3. | トラックバック:0
  4. | コメント:0

アクリルサイン用の基板を実装しました

FusionPCBから基板が届いたので、部品を実装しました。実装に失敗してしまいましたが、電気的にはつながっているので、良しとしました。動作は問題なかったです。PWMでぼわーと点灯、ぼわーと消灯。7色に変化します。明るいですよ。
acrylic_signs_15_121104.jpg

今日、アクリルを加工して、1台完成させようと思っています。
  1. 2012年11月04日 04:16 |
  2. Make出展
  3. | トラックバック:0
  4. | コメント:0

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する18(再度ACPポートを使用)

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する17(ARMのソフトからキャラクタを書けた2)”の続き。

またまたツイッターでACPポートの使用方法がTRMに書いてあると教えてもらったで、やってみることにした。
マニュアルは”Zynq-7000 All Programmable SoC テクニカル リファレンス マニュアル UG585 (v1.6.1) 2013 年 9 月 10 日”の96ページに、ACP の要求として、

コヒーレントな ACP 読み出し要求 : ARVALID と一緒に ARUSER[0] = 1 および ARCACHE[1] = 1 が送信された場合、ACP 読み出し要求はコヒーレントです。

コヒーレントな ACP 書き込み要求 : AWVALID と共に AWUSER[0] = 1 および AWCACHE[1] = 1 が送信 された場合、ACP 書き込み要求はコヒーレントです。


ということで、

assign M_AXI_ARCACHE = 4'b0010; // Normal Non-cacheable Non-bufferable
assign M_AXI_ARUSER = 1'b1; // コヒーレントな ACP 読み出し要求


にして、もう一度、”ZedBoardにビットマップ・ディスプレイ・コントローラを追加する8(ACPを使用)”を見ながら、ビットマップ・ディスプレイ・コントローラで使用するAXIバスのポートをACPポートに変更しました。そうすると、やはりキャラクタの表示波形が乱れた。その様子を図1に示す。
(ACPポートはキャッシュのコヒーレンシを維持できるAXIバスのポートだ。キャッシュのコヒーレンシを維持できれば、AXIバス経由でDDR3 SDRAMにReadする時に、キャシュにReadするアドレスの更新されたデータがあれば、AXIバスのReadをWaitさせて、キャッシュからDDR3 SDRAMにcast outし、その後にプロセッサで更新された正しい値をAXIバス経由でDMAできるはずだ。)
ZedBoad_BitMap_DispCnt_101_121103.jpg
図1 ACPポートを使用したキャラクタ表示(C_M_AXI_SUPPORTS_USER_SIGNALSにチェックなし)

もう一度、XPSプロジェクトを見なおしてみると、bitmap_disp_cntrler_axi_master_0 IPコアのプロパティで、C_M_AXI_SUPPORTS_USER_SIGNALSにチェックが入っていなかったので、チェックを入れた。これは、ユーザー信号をサポートするかどうかを決めるチェックだった。チェックを入れると、下の5つのユーザー信号がハイドから回復した。(図2参照)
ZedBoad_BitMap_DispCnt_103_121103.png
図2 bitmap_disp_cntrler_axi_master_0 IPコアのプロパティ

これで、論理合成、インプリメントを行なって、キャラクタを表示させたところ、だいぶましになったのだが、最初の行がおかしくなってしまった。何度、ソフトウェアを実行しても、おかしいパターンは変わるが、最初の行がいつもおかしい。その様子を図3に示す。
ZedBoad_BitMap_DispCnt_102_121103.jpg
図3 ACPポートを使用したキャラクタ表示(C_M_AXI_SUPPORTS_USER_SIGNALSにチェック入り)

実行しているソフトウェアは、キャッシュのフラッシュをコメントアウトしてあるが、このコメントアウトを外してキャッシュをソフトウェアでフラッシュすると、キャラクタがきちんと表示される。図1、図3を表示したソフトウェアを下に示す。

リスト1 キャッシュのフラッシュを全く行わないビットマップ・ディスプレイ・コントローラ用キャラクタ表示ソフトウェア

/* * drawn_disp.c * *  Created on: 2012/09/25 *      Author: Masaaki */

#include "xparameters.h"
#include "xgpio.h"

#define VIDEO_BUFFER_START_ADDRESS    0x10000000

void Xil_DCacheFlush(void);
void Xil_DCacheFlushLine(unsigned int adr);

// char_draw: アドレスとキャラクタコードを受け取ってそのキャラクタを描画する
// addr : 描画するキャラクタのスタートアドレス
// char_code : 描画するキャラクタのコード
// char_color : 描画するキャラクタのカラー、32ビットで表される、0RGBと8ビットずつで表す
// 戻り値:次のキャラクタの描画先頭アドレスを示す。
unsigned int *char_draw(unsigned int *addr, unsigned char char_code, unsigned int char_color){
    int i,j;
    unsigned int char_pattern;
    unsigned int char_code_int;
    unsigned int *cal_char_addr;
    unsigned int *return_addr;

    char_code_int = (unsigned int)(char_code);
    return_addr = addr + 8;
    cal_char_addr = (unsigned int *)(XPAR_CHAR_ROM_AXI_LITE_0_S_AXI_RNG00_BASEADDR+((char_code_int<<3)<<2)); // キャラジェネROMのデータは32ビット幅で下の8ビットだけ有効
    for(i=0; i<8; i++){
        char_pattern = *(volatile unsigned int *)(cal_char_addr); // キャラクタのパターンを読み出し
        for(j=0; j<8; j++){
            if(char_pattern & 0x1// 7ビット目が1の時はドットを描画
                *(volatile unsigned int *)((unsigned int)addr ^ 4) = char_color;
            else
                *(volatile unsigned int *)((unsigned int)addr ^ 4) = 0// 黒を描画
            // Xil_DCacheFlush();    // 1番目の時間計測
            //if (((unsigned int)addr & 0x1f) == 0x1c) // キャッシュラインの終わりで当該キャッシュラインをフラッシュ
                        //Xil_DCacheFlushLine((unsigned int)addr);
            addr++;
            char_pattern >>= 1// キャラクタのパターンを1ビット右シフト
        }
        addr -= 8// 行の最初のアドレスに戻す
        addr += 640// アドレスを1行下にする
        cal_char_addr++;
    }
    // Xil_DCacheFlush();    // 2番目の時間計測

    return return_addr;
}

int main()
{
    static XGpio GPIOInstance_Ptr;
    int xStatus;
    unsigned char char_code;
    unsigned int * ddr2_addr;
    unsigned int coler_code;
    unsigned int char_cnt;
    int i, j;

    // AXI GPIO Initialization
    xStatus = XGpio_Initialize(&GPIOInstance_Ptr,XPAR_AXI_GPIO_0_DEVICE_ID);
    if(XST_SUCCESS != xStatus)
        print("GPIO INIT FAILED\n\r");
    // AXI GPIO Set the Direction(Output setting)
    XGpio_SetDataDirection(&GPIOInstance_Ptr, 10);
    // init_doneに1を出力
    XGpio_DiscreteWrite(&GPIOInstance_Ptr, 11);

    for (ddr2_addr=(unsigned int *)VIDEO_BUFFER_START_ADDRESS; ddr2_addr<(unsigned int *)((unsigned int)VIDEO_BUFFER_START_ADDRESS + (unsigned int)0x12C000); ddr2_addr++){
        *(volatile unsigned int *)((unsigned int)ddr2_addr ^ 4) = 0;
        //if (((unsigned int)ddr2_addr & 0x1f) == 0x1c) // キャッシュラインの終わりで当該キャッシュラインをフラッシュ
            //Xil_DCacheFlushLine((unsigned int)ddr2_addr);
    }
    // Xil_DCacheFlush();

    ddr2_addr = (unsigned int *)VIDEO_BUFFER_START_ADDRESS;
    char_cnt = 0;
    for(j=0; j<8; j++){
        for(i=1; i<8; i++){
            switch(i){
                case 1 :
                    coler_code = 0xff;
                    break;
                case 2 :
                    coler_code = 0xff00;
                    break;
                case 3 :
                    coler_code = 0xffff;
                    break;
                case 4 :
                    coler_code = 0xff0000;
                    break;
                case 5 :
                    coler_code = 0xff00ff;
                    break;
                case 6 :
                    coler_code = 0xffff00;
                    break;
                case 7 :
                    coler_code = 0xffffff;
            }

            for(char_code=0x21; char_code<0x80; char_code++){
                if(char_code >= 0x80// キャラクタコードが上限に達したら、一番下に戻す
                    char_code = 0x21;
                if (char_cnt!=0 && char_cnt%80==0)
                    ddr2_addr = (unsigned int *)((unsigned int)ddr2_addr + 640*4*7); // 1行書き終わったので、下の行に、アドレスは640ドットx1ピクセル4バイトx7行
                ddr2_addr = char_draw(ddr2_addr, char_code, coler_code); // キャラクタを描画
                char_cnt++;
            }
        }
    }

    //Xil_DCacheFlush();    // 3番めの時間計測
    return 0;
}


リスト1で、if行と共に、Xil_DCacheFlushLine((unsigned int)ddr2_addr); をコメントアウトから回復させると、正常にキャラクタが表示された。
ACPポートを使用すると、なんとも微妙な結果になってしまった。

(2013/10/12:追記)
やっとACPポートがおかしい理由がわかりました。

XPSを立ちあげて、Zynqタブの 64b AXI ACP Slave Port をクリックします。
ZedBoad_BitMap_DispCnt_105_131012.png

ダイアログの Accelerator Coherency Port (ACP) Slave AXI Interface の Use slave driven AxUSER values にチェックを入れます。
ZedBoad_BitMap_DispCnt_106_131012.png

これで、キャッシュのフラッシュのないソフトウェアを走らせると、うまくキャラクタが表示できました。これを忘れていたようです。
ZedBoad_BitMap_DispCnt_104_131012.jpg

ツイッターで、Dyrell6502さんに教えていただきました。本当にありがとうございました。長年の課題が解消出来ました。

(もう一度、追記)
その後、Accelerator Coherency Port (ACP) Slave AXI Interface の Use slave driven AxUSER values のチェックを外しても完全に動作しました。PlanAhead14.2からPlanAhead14.6に移行したのが良かったのかもしれません?
ですが、やはりチェックを入れたほうが良いと思うので、入れておきたいと思います。
  1. 2012年11月03日 05:41 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0

FusionPCBから基板が届きました

今日、FusionPCBから基板が届きました。10月15日に注文したので、18日くらいで届きました。3回頼んだ中で一番早かったです。
基板は板厚1mm、50mm x 100mm、レジスト色は赤です。やはり基板は結構薄いです。明日から基板に部品を載せて、早く光らせてみたいです。楽しみです。
acrylic_signs_14_121102.jpg

  1. 2012年11月02日 21:34 |
  2. Make出展
  3. | トラックバック:0
  4. | コメント:1

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する17(ARMのソフトからキャラクタを書けた2)

ZedBoardにビットマップ・ディスプレイ・コントローラを追加する16(ARMのソフトからキャラクタを書けた)”の続き。

前回、ZynqのARMプロセッサのソフトウェアからビットマップ・ディスプレイ・コントローラに文字を書くことが出来た。その際使用した関数はXil_DCacheFlush(); だった。これはソースを読んでみるとL1, L2キャッシュをすべてフラッシュするようだ。これでは、画像フレームバッファをキャッシュするキャッシュラインだけでなく他のキャッシュラインもフラッシュされてしまう。これでは無駄が大きい。その内に、Xil_DCacheFlushLine((unsigned int)addr); という関数をツイッターで教えて頂いた。これだったら、当該キャッシュラインだけをフラッシュすることができるはずだ。
なお、Xil_DCacheFlushLine() のソースコードは、ISE14.2を使用している場合は、Xilinx\14.2\ISE_DS\EDK\sw\lib\bsp\standalone_v3_07_a\src\cortexa9\xil_cache.c にある。ツイッターで教えて頂いた。ありがとうございました。

ARMのキャッシュラインの幅は32バイトの様なので、文字をラスタ毎に書き込んでいる時のアドレスの下5ビットが0x1Cになったら、Xil_DCacheFlushLine() を行うこととする。この変更を加えて、drawn_disp.elf をSDKから実行したところ問題なく文字が表示された。やはり文字を表示するのが速い。
ZedBoad_BitMap_DispCnt_100_121101.jpg

下に、drawn_disp.c を示す。

/* * drawn_disp.c * *  Created on: 2012/09/25 *      Author: Masaaki */

#include "xparameters.h"
#include "xgpio.h"

#define VIDEO_BUFFER_START_ADDRESS    0x10000000

void Xil_DCacheFlush(void);
void Xil_DCacheFlushLine(unsigned int adr);

// char_draw: アドレスとキャラクタコードを受け取ってそのキャラクタを描画する
// addr : 描画するキャラクタのスタートアドレス
// char_code : 描画するキャラクタのコード
// char_color : 描画するキャラクタのカラー、32ビットで表される、0RGBと8ビットずつで表す
// 戻り値:次のキャラクタの描画先頭アドレスを示す。
unsigned int *char_draw(unsigned int *addr, unsigned char char_code, unsigned int char_color){
    int i,j;
    unsigned int char_pattern;
    unsigned int char_code_int;
    unsigned int *cal_char_addr;
    unsigned int *return_addr;

    char_code_int = (unsigned int)(char_code);
    return_addr = addr + 8;
    cal_char_addr = (unsigned int *)(XPAR_CHAR_ROM_AXI_LITE_0_S_AXI_RNG00_BASEADDR+((char_code_int<<3)<<2)); // キャラジェネROMのデータは32ビット幅で下の8ビットだけ有効
    for(i=0; i<8; i++){
        char_pattern = *(volatile unsigned int *)(cal_char_addr); // キャラクタのパターンを読み出し
        for(j=0; j<8; j++){
            if(char_pattern & 0x1// 7ビット目が1の時はドットを描画
                *(volatile unsigned int *)((unsigned int)addr ^ 4) = char_color;
            else
                *(volatile unsigned int *)((unsigned int)addr ^ 4) = 0// 黒を描画
            // Xil_DCacheFlush();    // 1番目の時間計測
            if (((unsigned int)addr & 0x1f) == 0x1c) // キャッシュラインの終わりで当該キャッシュラインをフラッシュ
                        Xil_DCacheFlushLine((unsigned int)addr);
            addr++;
            char_pattern >>= 1// キャラクタのパターンを1ビット右シフト
        }
        addr -= 8// 行の最初のアドレスに戻す
        addr += 640// アドレスを1行下にする
        cal_char_addr++;
    }
    // Xil_DCacheFlush();    // 2番目の時間計測

    return return_addr;
}

int main()
{
    static XGpio GPIOInstance_Ptr;
    int xStatus;
    unsigned char char_code;
    unsigned int * ddr2_addr;
    unsigned int coler_code;
    unsigned int char_cnt;
    int i, j;

    // AXI GPIO Initialization
    xStatus = XGpio_Initialize(&GPIOInstance_Ptr,XPAR_AXI_GPIO_0_DEVICE_ID);
    if(XST_SUCCESS != xStatus)
        print("GPIO INIT FAILED\n\r");
    // AXI GPIO Set the Direction(Output setting)
    XGpio_SetDataDirection(&GPIOInstance_Ptr, 10);
    // init_doneに1を出力
    XGpio_DiscreteWrite(&GPIOInstance_Ptr, 11);

    for (ddr2_addr=(unsigned int *)VIDEO_BUFFER_START_ADDRESS; ddr2_addr<(unsigned int *)((unsigned int)VIDEO_BUFFER_START_ADDRESS + (unsigned int)0x12C000); ddr2_addr++){
        *(volatile unsigned int *)((unsigned int)ddr2_addr ^ 4) = 0;
        if (((unsigned int)ddr2_addr & 0x1f) == 0x1c) // キャッシュラインの終わりで当該キャッシュラインをフラッシュ
            Xil_DCacheFlushLine((unsigned int)ddr2_addr);
    }
    // Xil_DCacheFlush();

    ddr2_addr = (unsigned int *)VIDEO_BUFFER_START_ADDRESS;
    char_cnt = 0;
    for(j=0; j<8; j++){
        for(i=1; i<8; i++){
            switch(i){
                case 1 :
                    coler_code = 0xff;
                    break;
                case 2 :
                    coler_code = 0xff00;
                    break;
                case 3 :
                    coler_code = 0xffff;
                    break;
                case 4 :
                    coler_code = 0xff0000;
                    break;
                case 5 :
                    coler_code = 0xff00ff;
                    break;
                case 6 :
                    coler_code = 0xffff00;
                    break;
                case 7 :
                    coler_code = 0xffffff;
            }

            for(char_code=0x21; char_code<0x80; char_code++){
                if(char_code >= 0x80// キャラクタコードが上限に達したら、一番下に戻す
                    char_code = 0x21;
                if (char_cnt!=0 && char_cnt%80==0)
                    ddr2_addr = (unsigned int *)((unsigned int)ddr2_addr + 640*4*7); // 1行書き終わったので、下の行に、アドレスは640ドットx1ピクセル4バイトx7行
                ddr2_addr = char_draw(ddr2_addr, char_code, coler_code); // キャラクタを描画
                char_cnt++;
            }
        }
    }

    //Xil_DCacheFlush();    // 3番めの時間計測
    return 0;
}


これで、Zynq-7000シリーズのARMプロセッサを使用して、ビットマップ・ディスプレイ・コントローラに文字を描画するソフトウェアは完成だ。
  1. 2012年11月02日 05:02 |
  2. ZedBoard
  3. | トラックバック:0
  4. | コメント:0