FC2カウンター FPGAの部屋 2019年01月25日
FC2ブログ

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

FPGAの部屋

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

Ultra96のDisplayPortを使用するためのプロジェクトを作成する4(完成)

Ultra96のDisplayPortを使用するためのプロジェクトを作成する3(デバック)”の続き。

前回は、Ultra96 のDisplayPort でテストパターンがうまく表示できなかったので、Vivado Analyzer を使ってデバックを行った。今回は、デバック結果を元にVerilog HDL ソースコードを修正して、テストパターンの表示を行ってみよう。

前回のVivado Analyzer によるデバックでは、ip_done が来た後でも、1クロック分 ip_start が余計に 1 にアサートされているのが問題だった。そのVerilog HDL ソースコードを”Ultra96のDisplayPortを使用するためのIPを作成する7(テストベンチ の作成3)"のaxis2video_out.v から拾ってみよう。

    // IP start State Machine
    always @(posedge axi_clk) begin
        if (!axi_rst_n) begin
            cs_start <= IDLE_START;
            ip_start <= 1'b0;
        end else begin
            case (cs_start)
                IDLE_START : begin
                    ip_start <= 1'b0;
                    if (vsync_falling_edge_axi)
                        cs_start <= IP_START_1;
                end
                IP_START_1 : begin
                    ip_start <= 1'b1;
                    if (ip_done)
                        cs_start <= IDLE_START;
                end
            endcase
        end
    end


IP start State Machine で ip_start を出力しているが、ムーア型のステートマシンで、ステートにip_start の値を書いてしまっている。この書き方だとステートに遷移してからip_start の値を出力するので、1クロック遅れてしまう。そこで、以下のようにIP start State Machine を書き換えた。

    // IP start State Machine
    always @(posedge axi_clk) begin
        if (!axi_rst_n) begin
            cs_start <= IDLE_START;
            ip_start <= 1'b0;
        end else begin
            case (cs_start)
                IDLE_START : begin
                    ip_start <= 1'b0;
                    if (vsync_falling_edge_axi) begin
                        cs_start <= IP_START_1;
                        ip_start <= 1'b1;
                    end
                end
                IP_START_1 : begin
                    if (ip_done) begin
                        cs_start <= IDLE_START;
                        ip_start <= 1'b0;
                    end
                end
            endcase
        end
    end


上のソースコードのように書くと、ステートの遷移と同時にip_start の値も変化する。
axi2video_out.v の全体を貼っておく。
(2019/02/07 修正:LiveVideo のビット・フィールドをBRG に変更)

// axis2video_out.v
// 2019/01/14 by marsee
// 2019/01/23 : Fixed IP start state machine bug
//

`default_nettype none

module axis2video_out
    (
        // Clock and Reset
        input wire  disp_clk,
        input wire  axi_clk,
        input wire  axi_rst_n,
        input wire  init_done,
        
        // AXI4-Stream
        input wire  [31:0] axis_tdata,
        input wire  axis_tvalid,
        output wire axis_tready,
        input wire  [3:0] axis_tkeep,
        input wire  [3:0] axis_tstrb,
        input wire  axis_tuser,
        input wire  axis_tlast,
        input wire  axis_tid,
        input wire  axis_tdest,
        
        // IP
        output reg  ip_start,
        input wire  ip_done,
        
        // video in
        input wire  de_in,
        input wire  vsync_in,
        input wire  hsync_in,
        
        // video_out
        output wire  [35:0] disp_pixel,
        output wire  de_out,
        output wire vsync_out,
        output wire  hsync_out
    );

    parameter       IDLE_START =    1'b0,
                    IP_START_1 =    1'b1;
    
    reg     reset_disp_2b = 1'b1, reset_disp_1b = 1'b1;
    wire    reset_disp;
    reg     fifo_reset_axi_2b = 1'b0, fifo_reset_axi_1b = 1'b0;
    wire    fifo_reset_axi;
    reg     fifo_reset_disp_2b = 1'b0, fifo_reset_disp_1b = 1'b0;
    wire    fifo_reset_disp;
    reg     de_1d, vsync_1d, hsync_1d;
    reg     vsync_axi_1b, vsync_axi_2b;
    wire    vsync_axi;
    reg     vsync_axi_1d, vsync_axi_2d;
    reg     cs_start;
    wire    pfifo_empty, pfifo_full;
    wire [33:0] pfifo_dout;
    reg    vsync_rising_edge_axi, vsync_falling_edge_axi;
    
    always @(posedge disp_clk) begin
        if(reset_disp) begin
            de_1d <= 1'b0;
            vsync_1d <= 1'b0;
            hsync_1d <= 1'b0;
        end else begin
            de_1d <= de_in;
            vsync_1d <= vsync_in;
            hsync_1d <= hsync_in;
        end
    end
    
    // reset signals    
    always @(posedge axi_clk) begin
        fifo_reset_axi_2b <= ~init_done | ~axi_rst_n | vsync_1d;
        fifo_reset_axi_1b <= fifo_reset_axi_2b;
    end
    assign fifo_reset_axi = fifo_reset_axi_1b;
        
    always @(posedge disp_clk) begin
        fifo_reset_disp_2b <= ~init_done | ~axi_rst_n | vsync_1d;
        fifo_reset_disp_1b <= fifo_reset_disp_2b;
    end
    assign fifo_reset_disp = fifo_reset_disp_1b;
        
    always @(posedge disp_clk) begin
        reset_disp_2b <= ~init_done | ~axi_rst_n;
        reset_disp_1b <= reset_disp_2b;
    end
    assign reset_disp = reset_disp_1b;
    
    // vsync_rising_edge, vsync_falling_edge
    always @(posedge axi_clk) begin
        if (!axi_rst_n) begin
            vsync_axi_2b <= 1'b1;
            vsync_axi_1b <= 1'b1;
        end else begin
            vsync_axi_2b <= vsync_1d;
            vsync_axi_1b <= vsync_axi_2b;
        end
    end
    assign vsync_axi = vsync_axi_1b;
    
    always @(posedge axi_clk) begin
        if (!axi_rst_n) begin
            vsync_axi_1d <= 1'b1;
            vsync_axi_2d <= 1'b1;
        end else begin
            vsync_axi_1d <= vsync_axi;
            vsync_axi_2d <= vsync_axi_1d;
        end
    end

    always @(posedge axi_clk) begin
        if (!axi_rst_n) begin
            vsync_rising_edge_axi = 1'b0;
            vsync_falling_edge_axi = 1'b0;
        end else begin
            vsync_rising_edge_axi <= ~vsync_axi_2d & vsync_axi_1d;
            vsync_falling_edge_axi <= vsync_axi_2d & ~vsync_axi_1d;
        end
    end
    
    // IP start State Machine
    always @(posedge axi_clk) begin
        if (!axi_rst_n) begin
            cs_start <= IDLE_START;
            ip_start <= 1'b0;
        end else begin
            case (cs_start)
                IDLE_START : begin
                    ip_start <= 1'b0;
                    if (vsync_falling_edge_axi) begin
                        cs_start <= IP_START_1;
                        ip_start <= 1'b1;
                    end
                end
                IP_START_1 : begin
                    if (ip_done) begin
                        cs_start <= IDLE_START;
                        ip_start <= 1'b0;
                    end
                end
            endcase
        end
    end
    
    // data width 34 bits, 512 depth
    pixel_fifo pixel_fifo_i (
        .wr_rst(fifo_reset_axi),
        .wr_clk(axi_clk),
        .rd_rst(fifo_reset_disp),
        .rd_clk(disp_clk),
        .din({axis_tuser, axis_tlast, axis_tdata}),
        .dout(pfifo_dout),
        .wr_en(~pfifo_full & axis_tvalid),
        .full(pfifo_full),
        .rd_en(de_1d),
        .empty(pfifo_empty)
    );
    assign axis_tready = ~pfifo_full;
    
    assign disp_pixel = {pfifo_dout[7:0], 4'd0, pfifo_dout[23:16], 4'd0, pfifo_dout[15:8], 4'd0}; //BRG
    assign de_out = de_1d;
    assign vsync_out = vsync_1d;
    assign hsync_out = hsync_1d;
endmodule

`default_nettype wire


axis2video_out.v の修正後にもう一度、Vivado Analyzer で波形を観察した。
axis_tuser の立ち上がりでトリガした波形を示す。axis_tuser の立ち上がりの前にip_start がアサートされている状態は無かった。これで正常になったと言える。
DisplayPort_test_107_19023.png

次に、ip_done の立ち上がりでトリガを掛けた。ip_done と同時に ip_start も落ちている。これも正常だ。
DisplayPort_test_108_19023.png

これで、DisplayPort の画像を見てみると、正常にテストパターンが表示されていた。やった〜〜〜!!!長かった。。。
DisplayPort_test_111_19025.jpg

2019/02/07 追記: テスト・パターンの色が違っています。詳しくは、”Zynq UltraScale+ MPSoC のDisplayPort のLiveVideo のピクセルデータ”を参照ください)

右の四角のほうが左よりも短い気がするが、これはディスプレイの特性かもしれない?
  1. 2019年01月25日 04:39 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0