FC2カウンター FPGAの部屋 Spartan-3A Starter KitでEDKを使ってカメラ表示13(ディスプレイ回路のHDLファイル)
FC2ブログ

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

FPGAの部屋

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

Spartan-3A Starter KitでEDKを使ってカメラ表示13(ディスプレイ回路のHDLファイル)

Spartan-3A Starter KitでEDKを使ってカメラ表示12(ディスプレイ回路のシミュレーション)”の続き。

今回は、下位モジュールを貼っておく。
まずは、VGA_Display_Controller.v 。

// VGA Display Controller
// SRAMから24MHz、16bit幅で2回に1回読みだされた輝度情報をRGBに変換してカラーで表示する
// UVデータを入力して、YUV-RGB変換を行う。変換式は以下のものを使用する
// R = (Y<<8 + "1_0110_0100"*V - X"B380")>>8
// G = (Y<<8 - "1011_0111"*V - "0101_1000"*U + X"8780")>>8
// B = (Y<<8 + "1_1100_0110"*U - X"E300")>>8
// 上の式で計算を行う。ビット長は19ビット
// YUVデータ保存用非同期FIFO, Wirte側32ビット幅128深度、Read側16ビット256深度とする。これはAFIFO_FULL_VALにwr_data_countに達したときにリクエストを止めると、その時点で要求されていたRead要求分のデータが帰ってくるためである
// RGB444用に変更

`default_nettype none

// synthesis translate_off
// `include "std_ovl_defines.h"
// synthesis translate_on

module VGA_Display_Controller #(
    parameter AFIFO_FULL_VAL = 256,
    parameter AFIFO_HALF_FULL_VAL = 128
)
(
    input    wire    clk_disp,    // 25MHzのディスプレイ表示用クロック
    input    wire    clk_ddr2,    // 133.33MHzのDDR2 SDRAM用クロック
    input    wire    reset_disp,    // clk_disp 用リセット
    input    wire    reset_ddr2,    // clk_ddr2 用リセット
    output    reg        request,        // データ転送のrequest
    input    wire    grant,        // データ転送のgrant
    output    wire    [18:0]    address,    // DDR2 SDRAMのアドレス
    output    reg        req_we,        // Read要求のWrite Enable
    input    wire    addr_fifo_full,    // Read要求用FIFOのFULL
    input    wire    [31:0]    data_in,    // DDR2 SDRAMの画像データ(YUV)
    input    wire    data_valid,
    output    reg        [7:0]    red_out,
    output    reg        [7:0]    green_out,
    output    reg      [7:0]    blue_out,
    output    reg     hsyncx,
    output    reg     vsyncx,
    output    wire    afifo_overflow, // 非同期FIFO のオーバーフロー・エラー
    output    wire    afifo_underflow,    // 非同期FIFO のアンダーフロー・エラー
    output    reg        addr_is_zero,    // for test
    output    reg        h_v_is_zero        // for test
);
    `include "./disp_timing_parameters.vh"
    
    // parameter AFIFO_FULL_VAL = 10'b01_0000_0000; // Write側の値。Write側は32ビットなので、64でRead側は128となる
    // parameter AFIFO_HALF_FULL_VAL = 10'b00_1000_0000; // Write側の値。Write側は32ビットなので、32でRead側は64となる
    
    parameter [3:0]    idle_rdg=            4'b0001,
                    init_full_mode=        4'b0010,
                    wait_half_full=        4'b0100,
                    req_burst=            4'b1000;
    reg    [3:0] cs_rdg;
    parameter [1:0]    req_we_low=        2'b01,
                    req_we_high=    2'b10; // active
    reg [1:0] cs_vrw;
    reg afifo_rd_en;
    wire [15:0] afifo_dout;
    wire afifo_full;
    wire afifo_empty;
    wire [9:0] wr_data_count;
    reg [18:0] addr_count;
    wire hv_count_enable;
    reg [9:0] read_count;
    reg hv_cnt_ena_d1, hv_cnt_ena_d2;
    reg [10:0] h_count;
    reg [9:0] v_count;
    reg [7:0] red_node, green_node, blue_node;
    reg hsyncx_node, vsyncx_node;
    reg [15:0] RGBX;
    reg addr_is_zero_node, h_v_is_zero_node;
    
    parameter VGA_CON_STAR_ADDR = 8;
    
    // synthesis translate_off
//    wire [`OVL_FIRE_WIDTH-1:0] fire_overflow, fire_underflow;
    // synthesis translate_on
    
    // YUVデータ保存用非同期FIFO, Wirte側32ビット幅128深度、Read側16ビット256深度とする
    cam_data_afifo camd_afifo_inst (
        .rst(reset_ddr2),
        .wr_clk(clk_ddr2),
        .rd_clk(clk_disp),
        .din(data_in), // Bus [31 : 0] 
        .wr_en(data_valid),
        .rd_en(afifo_rd_en),
        .dout(afifo_dout), // Bus [15 : 0] 
        .full(afifo_full),
        .overflow(afifo_overflow),
        .empty(afifo_empty),
        .underflow(afifo_underflow),
        .wr_data_count(wr_data_count) // ouput [9 : 0] wr_data_count
    );
    
    // DDR2 SDRAMのアドレスカウンタ(DDR2 SDRAMクロックドメイン)カウンタの単位は16ビット=1画素(UYまたはVY)
    always @(posedge clk_ddr2) begin
        if (reset_ddr2)
            addr_count <= VGA_CON_STAR_ADDR;
        else begin
            if (addr_count>=(H_ACTIVE_VIDEO * V_ACTIVE_VIDEO)) // 1フレーム分描画終了したので、0にクリアする
                addr_count <= 0;
            else if (req_we) // DDR2 SDRAMへのRead要求のweがでたらカウントアップ
                addr_count <= addr_count + 19'd4; // DDR2 SDRAMは4バーストするので、+4
        end
    end
    assign address = addr_count;
    
    // Read コマンド生成モジュール
    
    // Read コマンド生成モジュール用ステートマシン
    always @(posedge clk_ddr2) begin
        if (reset_ddr2)
            cs_rdg <= idle_rdg;
        else begin
            case (cs_rdg)
                idle_rdg :
                    cs_rdg <= init_full_mode;
                init_full_mode : // 最初にcam_data_afifo をFULLにするステート、このステートではVGA信号は出力しないで、ひたすらcam_data_afifo がFULLになるのを待つ。
                    if (wr_data_count>=AFIFO_FULL_VAL)
                        cs_rdg <= wait_half_full;
                    else
                        cs_rdg <= init_full_mode;
                wait_half_full : // cam_data_afifo がHALF_FULLになるまでこのステートで待機
                    if (wr_data_count<=AFIFO_HALF_FULL_VAL)
                        cs_rdg <= req_burst;
                    else
                        cs_rdg <= wait_half_full;
                req_burst :
                    if (read_count==0 && wr_data_count>AFIFO_HALF_FULL_VAL) // VRAMのREAD要求をカウントするカウンタが0になって、wr_data_count がHALF_FULL以上になったら
                        cs_rdg <= wait_half_full;
                    else
                        cs_rdg <= req_burst;
            endcase
        end
    end
    assign hv_count_enable = (cs_rdg==wait_half_full || cs_rdg==req_burst) ? 1'b1 : 1'b0;
    
    // request の実装。VRAMのデータをReadしたいときにアクティベート。アービタへ
    always @(posedge clk_ddr2) begin
        if (reset_ddr2)
            request <= 1'b0;
        else begin
            if (cs_rdg==req_burst || cs_rdg==init_full_mode)
                request <= 1'b1;
            else
                request <= 1'b0;
        end
    end
    
    // read_count の実装、DDR2 SDRAM へのRead要求のカウントをする。
    always @(posedge clk_ddr2) begin
        if (reset_ddr2)
            read_count <= AFIFO_HALF_FULL_VAL;
        else begin
            if (cs_rdg==wait_half_full)
                read_count <= AFIFO_HALF_FULL_VAL;
            else if (cs_rdg==req_burst) begin
                if (read_count!=0 && addr_fifo_full==1'b0) // 0になるまで、VRAMへの要求アドレスFIFOがFULLでない時に要求を書き込む
                    read_count <= read_count - 1;
            end
        end
    end
    
    // req_we の実装、req_we はクロック2回に1回出力する。こうすることによってaddr_fifo_full のみでも、req_we をFF出力とすることができる。READのレイテンシは長いので、2回に1回のRead要求発行でも問題ないはず。。。
    always @(posedge clk_ddr2) begin
        if (reset_ddr2) begin
            cs_vrw <= req_we_low;
            req_we <= 1'b0;
        end else begin
            case (cs_vrw)
                req_we_low :
                    if ((cs_rdg==req_burst || cs_rdg==init_full_mode) && read_count!=0 && ~addr_fifo_full && grant) begin // 最初にpixel_async_fifo をFULLにする時とHALFまで行ってFULLにするとき
                        cs_vrw <= req_we_high;
                        req_we <= 1'b1;
                    end
                req_we_high : begin
                    cs_vrw <= req_we_low;
                    req_we <= 1'b0;
                end
            endcase
        end
    end
    // Read コマンド生成モジュール,終了    
    
    // ビットマップVGAコントローラのclk_disp (25MHz)動作部
    
    // h_count、v_count用に133.33MHz動作のcs_rdg の値を使用するので2回25MHz動作のFFでラッチする
    always @(posedge clk_disp) begin
        if (reset_disp) begin
            hv_cnt_ena_d1 <= 1'b0;
            hv_cnt_ena_d2 <= 1'b0;
        end else begin
            hv_cnt_ena_d1 <= hv_count_enable;
            hv_cnt_ena_d2 <= hv_cnt_ena_d1;
        end
    end
    
    // h_countの実装(水平カウンタ)
    always @(posedge clk_disp) begin
        if (reset_disp)
            h_count <= 0;
        else if (h_count>=(H_SUM-1)) // h_count がH_SUM-1よりも大きければ0に戻す(mod H_SUM)
            h_count <= 0;
        else if (hv_cnt_ena_d2) // 最初に非同期FIFOをフルにするまではカウントしない
            h_count <= h_count + 11'd1;
    end
    
    // v_countの実装(垂直カウンタ)
    always @(posedge clk_disp) begin
        if (reset_disp)
            v_count <= 0;
        else if (h_count>=(H_SUM-1)) begin // 水平カウンタがクリアされるとき
            if (v_count>=(V_SUM-1)) // v_count がV_SUM-1よりも大きければ0に戻す(mode V_SUM)
                v_count <= 0;
            else if (hv_cnt_ena_d2) // 最初に非同期FIFOをフルにするまではカウントしない
                v_count <= v_count + 10'd1;
        end
    end
    
    // RGBXを保存しておく
    always @(posedge clk_disp) begin // U
        if (reset_disp)
            RGBX <= 0;
        else
            RGBX <= afifo_dout;
    end

    // Red, Green, Blue出力
    always @(posedge clk_disp) begin
        if (reset_disp) begin
            red_node <= 0;
            green_node <= 0;
            blue_node <= 0;
        end else begin
            if (~hv_cnt_ena_d2) begin // 最初にpixel_async_fifo がフルになるまで画像データを出力しない。
                red_node <= 0;
                green_node <= 0;
                blue_node <= 0;
            end else if (h_count<H_ACTIVE_VIDEO && v_count<V_ACTIVE_VIDEO) begin // 表示している期間のみ画像データを出力する R 11:8 G 15:12 B 7:4
                // red_node <= {RGBX[11:8], 4'b000};
                // green_node <= {RGBX[7:4], 4'b000};
                // blue_node <= {RGBX[3:0], 4'b000};
                red_node <= {RGBX[3:0], 4'b000};
                green_node <= {RGBX[7:4], 4'b000};
                blue_node <= {RGBX[11:8], 4'b000};
            end else begin
                red_node <= 0;
                green_node <= 0;
                blue_node <= 0;
            end 
                
        end
    end
    always @(posedge clk_disp) begin
        if (reset_disp) begin
            red_out <= 0;
            green_out <= 0;
            blue_out <= 0;
        end else begin
            red_out <= red_node;
            green_out <= green_node;
            blue_out <= blue_node;
        end
    end
    
    // hsyncx 出力(水平同期信号)
    always @(posedge clk_disp) begin
        if (reset_disp)
            hsyncx_node <= 1'b1;
        else
            if (h_count>(H_ACTIVE_VIDEO + H_FRONT_PORCH-1) && h_count<=(H_ACTIVE_VIDEO + H_FRONT_PORCH + H_SYNC_PULSE-1)) // 水平同期期間
                hsyncx_node <= 1'b0;
            else
                hsyncx_node <= 1'b1;
    end
    always @(posedge clk_disp) begin
        if (reset_disp)
            hsyncx <= 1'b1;
        else
            hsyncx <= hsyncx_node;
    end
    
    // vsyncx 出力(水平同期信号)
    always @(posedge clk_disp) begin
        if (reset_disp)
            vsyncx_node <= 1'b1;
        else
            if (v_count>(V_ACTIVE_VIDEO + V_FRONT_PORCH-1) && v_count<=(V_ACTIVE_VIDEO + V_FRONT_PORCH + V_SYNC_PULSE-1)) // 垂直同期期間
                vsyncx_node <= 1'b0;
            else
                vsyncx_node <= 1'b1;
    end
    always @(posedge clk_disp) begin
        if (reset_disp)
            vsyncx <= 1'b1;
        else
            vsyncx <= vsyncx_node;
    end
    
    // afifo_rd_en の処理
    always @(posedge clk_disp) begin
        if (reset_disp)
            afifo_rd_en <= 1'b0;
        else begin
            if (~hv_cnt_ena_d2) // 初期化中
                afifo_rd_en <= 1'b0;
            else if (h_count<H_ACTIVE_VIDEO && v_count<V_ACTIVE_VIDEO) // 表示期間
                afifo_rd_en <= 1'b1;
            else
                afifo_rd_en <= 1'b0;
        end
    end

    
    // アサーション
    // synthesis translate_off
    always @ (posedge clk_ddr2) begin
        if (reset_ddr2)
            ;
        else begin
            if (afifo_overflow) begin 
                $display("%m: at time %t ERROR : FIFOがフルなのにライトした",$time);
                $stop;
            end
        end
    end
    always @(posedge clk_disp) begin
        if (reset_disp)
            ;
        else begin
            if (afifo_underflow) begin
                $display("%m: at time %t ERROR : FIFOが空なのにリードした",$time);
                $stop;
            end
        end
    end
    
    // ovl_never #(
        // `OVL_ERROR,            // severity_level
        // `OVL_ASSERT,        // property_type
        // "ERROR : FIFOがフルなのにライトした", // msg
        // `OVL_COVER_DEFAULT,    // coverage_level
        // `OVL_POSEDGE,        // clock_edge
        // `OVL_ACTIVE_HIGH,    // reset_polarity
        // `OVL_GATE_CLOCK    // gating_type
    // ) afifo_overflow_assertion (
        // clk_ddr2,
        // reset_ddr2,
        // 1'b1,
        // afifo_overflow,
        // fire_overflow
    // );
        
    // ovl_never #(
        // `OVL_ERROR,            // severity_level
        // `OVL_ASSERT,        // property_type
        // "ERROR : FIFOが空なのにリードした", // msg
        // `OVL_COVER_DEFAULT,    // coverage_level
        // `OVL_POSEDGE,        // clock_edge
        // `OVL_ACTIVE_HIGH,    // reset_polarity
        // `OVL_GATE_CLOCK    // gating_type
    // ) afifo_underflow_assertion (
        // clk_disp,
        // reset_disp,
        // 1'b1,
        // afifo_underflow,
        // fire_underflow
    // );
    // synthesis translate_on
    
    //  for test
    always @(posedge clk_ddr2) begin
        if (reset_ddr2) begin
            addr_is_zero_node <= 1'b0;
            addr_is_zero <= 1'b0;
        end else begin
            if (addr_count == 0)
                addr_is_zero_node <= 1'b1;
            else
                addr_is_zero_node <= 1'b0;
            addr_is_zero <= addr_is_zero_node;
        end
    end
    always @(posedge clk_disp) begin
        if (reset_disp) begin
            h_v_is_zero_node <= 1'b0;
            h_v_is_zero <= 1'b0;
        end else begin
            if (h_count==0 && v_count==0)
                h_v_is_zero_node <= 1'b1;
            else
                h_v_is_zero_node <= 1'b0;
            h_v_is_zero <= h_v_is_zero_node;
        end
    end
            
endmodule

`default_nettype wire


次は、disp_timing_parameters.vh 。

// 表示タイミングの定義

parameter H_ACTIVE_VIDEO= 640;
parameter H_FRONT_PORCH = 16;
parameter H_SYNC_PULSE = 96;
parameter H_BACK_PORCH = 48;
parameter H_SUM = H_ACTIVE_VIDEO + H_FRONT_PORCH + H_SYNC_PULSE + H_BACK_PORCH;

parameter V_ACTIVE_VIDEO = 480;
parameter V_FRONT_PORCH = 10;
parameter V_SYNC_PULSE = 2;
parameter V_BACK_PORCH = 29;
parameter V_SUM = V_ACTIVE_VIDEO + V_FRONT_PORCH + V_SYNC_PULSE + V_BACK_PORCH;

parameter H_DISPLAY_SIZE = H_ACTIVE_VIDEO/8; // 横80桁
parameter V_DISPLAY_SIZE = V_ACTIVE_VIDEO/8; // 縦60行

parameter RED_DOT_POS = 9; // 9ビット目がRED
parameter GREEN_DOT_POS = 8; // 8ビット目がGREEN
parameter BLUE_DOT_POS = 7; // 7ビット目がBLUE


次は、dcm_DISP_clk.v 。

// DCM module
// 62.5MHzを入力して、25MHzの表示用のclkを生成する(VGA)。

`default_nettype none
`timescale 1ns / 1ps

(* KEEP_HIERARCHY = "TRUE" *)module dcm_DISP_clk (sysclk, reset, clk_disp, dcm_locked_out);
    input    wire    sysclk;
    input    wire    reset;
    output    wire    clk_disp;
    output    wire    dcm_locked_out;
    
    wire clk_bufg, clk_node, dcm1_locked;
    wire clk_cam_node, clk_cam_bufg;
        
    DCM dcm_DDR2_clk_dcm (
        .CLKIN(sysclk),
        .CLKFB(clk_bufg),
        .DSSEN(1'b0),
        .PSINCDEC(1'b0),
        .PSEN(1'b0),
        .PSCLK(1'b0),
        .RST(reset),     // 前段のDCMがロックするまでリセット
        .CLK0(clk_node),
        .CLK90(),
        .CLK180(),
        .CLK270(),
        .CLK2X(),
        .CLK2X180(),
        .CLKDV(),
        .CLKFX(clk_cam_node),
        .CLKFX180(),
        .LOCKED(dcm1_locked),
        .PSDONE(),
        .STATUS()
    );
    defparam dcm_DDR2_clk_dcm.CLKIN_PERIOD = 16.0;
    defparam dcm_DDR2_clk_dcm.DLL_FREQUENCY_MODE = "LOW";
    defparam dcm_DDR2_clk_dcm.DUTY_CYCLE_CORRECTION = "TRUE";
    defparam dcm_DDR2_clk_dcm.CLKDV_DIVIDE = 2.0;
    defparam dcm_DDR2_clk_dcm.PHASE_SHIFT = 0;
    defparam dcm_DDR2_clk_dcm.CLKOUT_PHASE_SHIFT = "NONE";
    defparam dcm_DDR2_clk_dcm.STARTUP_WAIT = "FALSE";
    defparam dcm_DDR2_clk_dcm.CLKFX_DIVIDE = 5;
    defparam dcm_DDR2_clk_dcm.CLKFX_MULTIPLY = 2;
    
    BUFG CLK_BUFG_INST (
        .I(clk_node),
        .O(clk_bufg)
    );
    
    BUFG CLK_CAM_BUFG_INST (
        .I(clk_cam_node),
        .O(clk_cam_bufg)
    );
    assign dcm_locked_out = dcm1_locked;
    assign clk_disp = clk_cam_bufg;
endmodule

`default_nettype wire


最後に、cam_data_afifo.xco 。

##############################################################
#
# Xilinx Core Generator version 13.2
# Date: Thu Sep 08 19:15:36 2011
#
##############################################################
#
# This file contains the customisation parameters for a
# Xilinx CORE Generator IP GUI. It is strongly recommended
# that you do not manually alter this file as it may cause
# unexpected and unsupported behavior.
#
##############################################################
#
# Generated from component: xilinx.com:ip:fifo_generator:6.1
#
##############################################################
#
# BEGIN Project Options
SET addpads = false
SET asysymbol = true
SET busformat = BusFormatAngleBracketNotRipped
SET createndf = false
SET designentry = VHDL
SET device = xc3s700a
SET devicefamily = spartan3a
SET flowvendor = Foundation_ISE
SET formalverification = false
SET foundationsym = false
SET implementationfiletype = Ngc
SET package = fg484
SET removerpms = false
SET simulationfiles = Behavioral
SET speedgrade = -4
SET verilogsim = false
SET vhdlsim = true
# END Project Options
# BEGIN Select
SELECT Fifo_Generator family Xilinx,_Inc. 6.1
# END Select
# BEGIN Parameters
CSET almost_empty_flag=false
CSET almost_full_flag=false
CSET component_name=cam_data_afifo
CSET data_count=false
CSET data_count_width=9
CSET disable_timing_violations=false
CSET dout_reset_value=0
CSET empty_threshold_assert_value=4
CSET empty_threshold_negate_value=5
CSET enable_ecc=false
CSET enable_int_clk=false
CSET enable_reset_synchronization=true
CSET fifo_implementation=Independent_Clocks_Block_RAM
CSET full_flags_reset_value=1
CSET full_threshold_assert_value=509
CSET full_threshold_negate_value=508
CSET inject_dbit_error=false
CSET inject_sbit_error=false
CSET input_data_width=32
CSET input_depth=512
CSET output_data_width=16
CSET output_depth=1024
CSET overflow_flag=true
CSET overflow_sense=Active_High
CSET performance_options=First_Word_Fall_Through
CSET programmable_empty_type=No_Programmable_Empty_Threshold
CSET programmable_full_type=No_Programmable_Full_Threshold
CSET read_clock_frequency=1
CSET read_data_count=false
CSET read_data_count_width=11
CSET reset_pin=true
CSET reset_type=Asynchronous_Reset
CSET underflow_flag=true
CSET underflow_sense=Active_High
CSET use_dout_reset=true
CSET use_embedded_registers=false
CSET use_extra_logic=true
CSET valid_flag=false
CSET valid_sense=Active_High
CSET write_acknowledge_flag=false
CSET write_acknowledge_sense=Active_High
CSET write_clock_frequency=1
CSET write_data_count=true
CSET write_data_count_width=10
# END Parameters
GENERATE
# CRC: 2418f0c5


次は、user_logic.vhd とDisp_Controller_Top.vhd を接続する。

  1. 2011年09月11日 05:12 |
  2. EDK
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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