FC2カウンター FPGAの部屋 ZedBoardでHDMI出力14(YCbCr4:2:2 変換)
FC2ブログ

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

FPGAの部屋

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

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

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
http://marsee101.blog.fc2.com/tb.php/2319-78f0d47a
この記事にトラックバックする(FC2ブログユーザー)