FC2カウンター FPGAの部屋 ZedBoardでHDMI出力11(ビットマップ・ディスプレイ・コントローラ単体テスト)
FC2ブログ

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

FPGAの部屋

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

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

コメント

コメントの投稿


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

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