-03-LVDS输出接口设计【Xilinx-LVDS读写功能实现】

在-01-OV7251摄像头与设计规划【Xilinx-LVDS读写功能实现】中将设计分为了几个步骤,下面将介绍OV7251 LVDS信号模拟输出功能的逻辑设计 。

模拟摄像头的输出信号主要是为了方便前期测试和验证,而且本身SerDes的输出信号设计相对输入来说,复杂度要低很多。就按照前一节有提到的OV7251摄像头的时序来模拟出视频信号输出的信号。如下图:
这里写图片描述

输出模块分为以下两个部分:
- OV7251 LVDS Output Simulation 负责将并行的视频输出信号转化为串行的高速LVDS信号
- Video Test Pattern 负责模拟生成出一个动态的视频数据流

step1. Video Test Pattern

对外的接口:

module video_pattern_gen(
    input reset,
    input pixel_clk,
    input [9:0] pixel_data,
    input pattern_gen_enable,
    input ext_frame_sync,
    output reg [9:0] data_send
    );

主要的几个信号,
pixel_clk:像素时钟60MHz
pattern_gen_enable:模拟视频输出使能
ext_frame_sync:帧同步
data_send:像素灰度/同步码数据

因为最后模拟出的视频输出信号是内部包含的高速串行数据,所以Video Test Pattern这个模块不需要生成额外的场、行同步和消隐信号线,所有的SAV,EAV,dummy都包含在了data_send的10位并行数据当中。
在模块中,使用随时间渐变的灰度图块来模拟动态视频。

step2. OV7251 LVDS Output Simulation

对外接口:

module LVDS_OUT_DDR(
    //system clock and reset input
    input sys_clock,
    input sys_rst,
    //lvds output signals
    output clkout_p,
    output clkout_n,
    output dataout_p,
    output dataout_n,
    //enable pattern generate
    input pattern_gen_enable,
    input ext_frame_sync,
    output tx_clk,          //600MHz/10=60MHz clock frequency
    output [9:0] tx_dataout, 
    input [9:0] tx_datain,  //CCIR656 standard input 3FF 000 000 SAV ---- 3FF 000 000 EAV
    output sys_clock_200ref
    );

首先使用MMCM将输入的100MHz时钟进行倍频和分频处理,分别得到了LVDS串行输出用的的300MHz时钟,每像素数据用到的60MHz时钟,IDELAYCTRL用到的200MHz参考时钟。因为相机的LVDS数据与时钟是DDR传输方式,所以10bit的串行数据,时钟的频率将会是像素时钟频率的10/2=5倍,300MHz = 5 * 60MHz。
针对前一步中生成的data_send 10bit并行数据,使用两个OSERDESE2级联实现10bit数据的串行输出。其中边沿模式配置为DDR模式。OSERDESE2的输出,通过OBUFDS实现单端到差分信号的转换。

模拟LVDS视频输出接口模块的框图如下:
-03-LVDS输出接口设计【Xilinx-LVDS读写功能实现】_第1张图片

代码:

`timescale 1ps / 1ps

module video_pattern_gen(
    input reset,
    input pixel_clk,
    input [9:0] pixel_data,
    input pattern_gen_enable,
    input ext_frame_sync,
    output reg [9:0] data_send
    );

reg [9:0] pattern_data;

always@(posedge pixel_clk, posedge reset)
begin
    if(reset == 1'b1) begin
        data_send <= 10'd0;
    end
    else begin
        data_send <= (pattern_gen_enable == 1'b1)? pattern_data: pixel_data;
    end
end

//pattern data generate
//sync the frame data with input signal: ext_frame_sync
parameter PATTERN_H_ACTIVE_SIZE = 10'd640;
parameter PATTERN_H_BLANK_SIZE  = 10'd280;
parameter PATTERN_V_ACTIVE_SIZE = 10'd480;
parameter PATTERN_V_BLANK_SIZE  = 10'd36;

reg [9:0] pixel_data_add_reg;
reg [1:0] fsync_edge_buf;
always @(posedge pixel_clk, posedge reset)
begin
    if(reset == 1'b1) begin
        pixel_data_add_reg <= 10'h00;
        fsync_edge_buf <= 2'b00;
    end
    else begin
        fsync_edge_buf <= {fsync_edge_buf[0],ext_frame_sync};
        if(fsync_edge_buf == 2'b01) begin
            pixel_data_add_reg <= pixel_data_add_reg + 10'h8;
        end
    end
end

localparam [10:0] SYNC_CODE_SAV_ACTIVE = {10'h200},
                  SYNC_CODE_EAV_ACTIVE = {10'h240},
                  SYNC_CODE_SAV_BLANK  = {10'h2AC},
                  SYNC_CODE_EAV_BLANK  = {10'h2D8},
                  DUMMY_DATA0           = {10'h200},
                  DUMMY_DATA1           = {10'h040};
reg [3:0] pattern_state;
reg [9:0] h_count;
reg [9:0] v_count;
always @(posedge pixel_clk, posedge reset)
begin
    if(reset == 1'b1) begin
        h_count <= 10'd0;
        v_count <= 10'd0;
        pattern_state <= 4'd0;
        pattern_data <= 10'd0;
    end
    else begin
        case(pattern_state)
        4'd0: begin
            if(pattern_gen_enable == 1'b1) begin
                pattern_state <= 4'd1;
                h_count <= 10'd0;
                v_count <= 10'd0;
                pattern_data <= 10'd0;
            end
        end
        4'd1: begin
            h_count <= 10'd0;
            v_count <= 10'd0;
            pattern_data <= 10'd0;
            if(ext_frame_sync == 1'b1) begin
                pattern_state <= 4'd2;
            end
        end
        4'd2: begin    //SAV
            h_count <= h_count + 10'd1;                
            if(h_count >= 10'd927) begin
                h_count <= 10'd0;
                v_count <= v_count + 10'd1;
                if(v_count >= 10'd515) begin
                    v_count <= 10'd0;
                    pattern_state <= 4'd1;
                end
            end
            if(v_count < 10'd480) begin
                case(h_count)
                    10'd0, 10'd644:                     pattern_data <= 10'h3FF;
                    10'd1, 10'd2, 10'd645, 10'd646:     pattern_data <= 10'h000;
                    10'd3:                              pattern_data <= SYNC_CODE_SAV_ACTIVE;
                    10'd647:                            pattern_data <= SYNC_CODE_EAV_ACTIVE;
                    default: begin
                        if(h_count >= 10'd648) begin
                            pattern_data <= (h_count[0]==1'b0)? DUMMY_DATA0: DUMMY_DATA1;
                        end
                        else begin
                            if((h_count-10'd4) < 10'd128) begin
                                pattern_data <= 10'h3FF + pixel_data_add_reg;
                            end
                            else if((h_count-10'd4) < 10'd256) begin
                                pattern_data <= 10'h37F + pixel_data_add_reg;
                            end
                            else if((h_count-10'd4) < 10'd384) begin
                                pattern_data <= 10'h2FF + pixel_data_add_reg;
                            end
                            else if((h_count-10'd4) < 10'd512) begin
                                pattern_data <= 10'h27F + pixel_data_add_reg;
                            end
                            else begin
                                pattern_data <= 10'h1FF + pixel_data_add_reg;
                            end
                        end
                    end
                endcase
            end
            else if(v_count == 10'd480) begin
                case(h_count)
                    10'd0:            pattern_data <= 10'h3FF;
                    10'd1, 10'd2:     pattern_data <= 10'h000;
                    10'd3:            pattern_data <= SYNC_CODE_SAV_BLANK;
                    default: begin
                        pattern_data <= (h_count[0]==1'b0)? DUMMY_DATA0: DUMMY_DATA1;
                    end
                endcase
            end
            else if(v_count < 10'd515) begin
                pattern_data <= (h_count[0]==1'b0)? DUMMY_DATA0: DUMMY_DATA1;
            end
            else if(v_count == 10'd515) begin
                case(h_count)
                    10'd644:            pattern_data <= 10'h3FF;
                    10'd645, 10'd646:   pattern_data <= 10'h000;
                    10'd647:            pattern_data <= SYNC_CODE_EAV_BLANK;
                    default: begin
                        pattern_data <= (h_count[0]==1'b0)? DUMMY_DATA0: DUMMY_DATA1;
                    end
                endcase        
            end
        end
        endcase
    end
end

endmodule
`timescale 1ps / 1ps

module LVDS_OUT_DDR(
    //system clock and reset input
    input sys_clock,
    input sys_rst,
    //lvds output signals
    output clkout_p,
    output clkout_n,
    output dataout_p,
    output dataout_n,
    //enable pattern generate
    input pattern_gen_enable,
    input ext_frame_sync,
    output tx_clk,          //600MHz/10=60MHz clock frequency
    output [9:0] tx_dataout, 
    input [9:0] tx_datain,  //CCIR656 standard input 3FF 000 000 SAV ---- 3FF 000 000 EAV
    output sys_clock_200ref
    );

//high speed clock
wire pixel_clk ;        // Pixel clock for data output serdes        txpllmmcm_x1        100M
//tx clk
BUFG BUFG_tx_clk (
  .O(tx_clk), // 1-bit output: Clock output
  .I(pixel_clk)  // 1-bit input: Clock input
);



//mmcm reset
wire mmcm_lckd;
wire not_tx_mmcm_lckd;
assign not_tx_mmcm_lckd = ~mmcm_lckd;
wire mmcm_reset;
assign mmcm_reset = sys_rst;

//phr reset , serdes reset
wire reset;
assign reset = not_tx_mmcm_lckd;

reg [3:0] count ;
reg reset_intr;
initial reset_intr = 1'b1 ;

always @ (posedge pixel_clk, posedge reset) // local reset
begin
if (reset == 1'b1) begin
    reset_intr <= 1'b1 ;
    count <= 4'h0 ;
end
else begin
    count <= count + 4'h1 ;
    if (count == 4'hF) begin
        reset_intr <= 1'b0 ;
        count <= count;
    end
end
end

//pattern data generate
//sync the frame data with input signal: ext_frame_sync
video_pattern_gen video_pattern_gen_inst(
    .reset(reset),
    .pixel_clk(pixel_clk),
    .pixel_data(tx_datain),
    .pattern_gen_enable(pattern_gen_enable),
    .ext_frame_sync(ext_frame_sync),
    .data_send(tx_dataout)
    );

//diff data output

//serdes data output
wire tx_data_out;
wire cascade_di;
wire cascade_ti;

wire [9:0] mdataina;
assign mdataina = tx_dataout;

OBUFDS io_data_out (
    .O          (dataout_p),
    .OB         (dataout_n),
    .I          (tx_data_out));

OSERDESE2 #(
    .DATA_WIDTH         (10),           // SERDES word width
    .TRISTATE_WIDTH     (1),
    .DATA_RATE_OQ       ("DDR"),        // , DDR
    .DATA_RATE_TQ       ("SDR"),        // , DDR
    .SERDES_MODE        ("MASTER"))     // <DEFAULT>, MASTER, SLAVE
oserdes_m (
    .OQ             (tx_data_out),
    .OCE            (1'b1),
    .CLK            (txclk),
    .RST            (reset_intr),
    .CLKDIV         (pixel_clk),
    .D8             (mdataina[2]),
    .D7             (mdataina[3]),
    .D6             (mdataina[4]),
    .D5             (mdataina[5]),
    .D4             (mdataina[6]),
    .D3             (mdataina[7]),
    .D2             (mdataina[8]),
    .D1             (mdataina[9]),
    .TQ             (),
    .T1             (1'b0),
    .T2             (1'b0),
    .T3             (1'b0),
    .T4             (1'b0),
    .TCE            (1'b1),
    .TBYTEIN        (1'b0),
    .TBYTEOUT       (),
    .OFB            (),
    .TFB            (),
    .SHIFTOUT1      (),
    .SHIFTOUT2      (),
    .SHIFTIN1       (cascade_di),
    .SHIFTIN2       (cascade_ti)) ;

OSERDESE2 #(
    .DATA_WIDTH         (10),           // SERDES word width.
    .TRISTATE_WIDTH     (1), 
    .DATA_RATE_OQ       ("DDR"),        // , DDR
    .DATA_RATE_TQ       ("SDR"),        // , DDR
    .SERDES_MODE        ("SLAVE"))      // <DEFAULT>, MASTER, SLAVE
oserdes_s (
    .OQ             (),
    .OCE            (1'b1),
    .CLK            (txclk),
    .RST            (reset_intr),
    .CLKDIV         (pixel_clk),
    .D8             (1'b0),
    .D7             (1'b0),
    .D6             (1'b0),
    .D5             (1'b0),
    .D4             (mdataina[0]),
    .D3             (mdataina[1]),
    .D2             (1'b0),
    .D1             (1'b0),
    .TQ             (),
    .T1             (1'b0),
    .T2             (1'b0),
    .T3             (1'b0),
    .T4             (1'b0),
    .TCE            (1'b1),
    .TBYTEIN        (1'b0),
    .TBYTEOUT       (),
    .OFB            (),
    .TFB            (),
    .SHIFTOUT1      (cascade_di),   
    .SHIFTOUT2      (cascade_ti),   
    .SHIFTIN1       (1'b0),         
    .SHIFTIN2       (1'b0)) ;


//diff clock output
parameter [9:0] clk_pattern = 10'b10_1010_1010;

wire tx_clk_out;
wire cascade_cdi;
wire cascade_cti;

OBUFDS io_clk_out (
    .O              (clkout_p),
    .OB             (clkout_n),
    .I              (tx_clk_out));

OSERDESE2 #(
    .DATA_WIDTH         (10),           // SERDES word width
    .TRISTATE_WIDTH     (1),
    .DATA_RATE_OQ       ("DDR"),        // , DDR
    .DATA_RATE_TQ       ("SDR"),        // , DDR
    .SERDES_MODE        ("MASTER"))         // <DEFAULT>, MASTER, SLAVE
oserdes_cm (
    .OQ             (tx_clk_out),
    .OCE            (1'b1),
    .CLK            (txclk),
    .RST            (reset_intr),
    .CLKDIV         (pixel_clk),
    .D8             (clk_pattern[2]),
    .D7             (clk_pattern[3]),
    .D6             (clk_pattern[4]),
    .D5             (clk_pattern[5]),
    .D4             (clk_pattern[6]),
    .D3             (clk_pattern[7]),
    .D2             (clk_pattern[8]),
    .D1             (clk_pattern[9]),
    .TQ             (),
    .T1             (1'b0),
    .T2             (1'b0),
    .T3             (1'b0),
    .T4             (1'b0),
    .TCE            (1'b1),
    .TBYTEIN        (1'b0),
    .TBYTEOUT       (),
    .OFB            (),
    .TFB            (),
    .SHIFTOUT1      (),
    .SHIFTOUT2      (),
    .SHIFTIN1       (cascade_cdi),
    .SHIFTIN2       (cascade_cti)) ;

OSERDESE2 #(
    .DATA_WIDTH         (10),           // SERDES word width.
    .TRISTATE_WIDTH     (1), 
    .DATA_RATE_OQ       ("DDR"),        // , DDR
    .DATA_RATE_TQ       ("SDR"),        // , DDR
    .SERDES_MODE        ("SLAVE"))          // <DEFAULT>, MASTER, SLAVE
oserdes_cs (
    .OQ             (),
    .OCE            (1'b1),
    .CLK            (txclk),
    .RST            (reset_intr),
    .CLKDIV         (pixel_clk),
    .D8             (1'b0),
    .D7             (1'b0),
    .D6             (1'b0),
    .D5             (1'b0),
    .D4             (clk_pattern[0]),
    .D3             (clk_pattern[1]),
    .D2             (1'b0),
    .D1             (1'b0),
    .TQ             (),
    .T1             (1'b0),
    .T2             (1'b0),
    .T3             (1'b0),
    .T4             (1'b0),
    .TCE            (1'b1),
    .TBYTEIN        (1'b0),
    .TBYTEOUT       (),
    .OFB            (),
    .TFB            (),
    .SHIFTOUT1      (cascade_cdi),  
    .SHIFTOUT2      (cascade_cti),  
    .SHIFTIN1       (1'b0),         
    .SHIFTIN2       (1'b0)) ;

//mmcm clock generated
parameter real CLKIN_PERIOD = 10.0; // clock period (ns) of input clock on clkin_p
parameter integer MMCM_MODE = 1 ;   // Parameter to set multiplier for MMCM to get VCO in correct operating range. 1 multiplies input clock by 7, 2 multiplies clock by 14, etc

wire clkint;
wire txpllmmcm_x1 ;             // pll generated x1 clock
wire txpllmmcm_xn ;             // pll generated xn clock
wire txpllmmcm_clkfb;
wire txpllmmcm_200ref;

BUFG BUFG_inst_0 (
   .O(clkint),    // 1-bit output: Clock output
   .I(sys_clock)  // 1-bit input: Clock input
);

MMCME2_ADV #(
      .BANDWIDTH            ("OPTIMIZED"),
      .CLKFBOUT_MULT_F      (6*MMCM_MODE),  //600M
      .CLKFBOUT_PHASE       (0.0),
      .CLKIN1_PERIOD        (CLKIN_PERIOD),
      .CLKIN2_PERIOD        (CLKIN_PERIOD),
      .CLKOUT0_DIVIDE_F     (2*MMCM_MODE),  //300M
      .CLKOUT0_DUTY_CYCLE   (0.5),
      .CLKOUT0_PHASE        (0.0),
      .CLKOUT1_DIVIDE       (10*MMCM_MODE), //60M
      .CLKOUT1_DUTY_CYCLE   (0.5),
      .CLKOUT1_PHASE        (0.0),
      .CLKOUT2_DIVIDE       (3*MMCM_MODE),  //200M
      .CLKOUT2_DUTY_CYCLE   (0.5),
      .CLKOUT2_PHASE        (0.0),//(90.0),
      .CLKOUT3_DIVIDE       (10*MMCM_MODE), //60M
      .CLKOUT3_DUTY_CYCLE   (0.5),
      .CLKOUT3_PHASE        (0.0),//(22.5),
      .CLKOUT4_DIVIDE       (10),
      .CLKOUT4_DUTY_CYCLE   (0.5),
      .CLKOUT4_PHASE        (0.0),
      .CLKOUT5_DIVIDE       (10),
      .CLKOUT5_DUTY_CYCLE   (0.5),
      .CLKOUT5_PHASE        (0.0),
      .COMPENSATION         ("ZHOLD"),
      .DIVCLK_DIVIDE        (1),
      .REF_JITTER1          (0.100))
tx_mmcme2_adv_inst (
      .CLKFBOUT         (txpllmmcm_clkfb),
      .CLKFBOUTB        (),
      .CLKFBSTOPPED     (),
      .CLKINSTOPPED     (),
      .CLKOUT0          (txpllmmcm_xn),
      .CLKOUT0B         (),
      .CLKOUT1          (txpllmmcm_x1),
      .CLKOUT1B         (),
      .CLKOUT2          (txpllmmcm_200ref),
      .CLKOUT2B         (),
      .CLKOUT3          (),
      .CLKOUT3B         (),
      .CLKOUT4          (),
      .CLKOUT5          (),
      .CLKOUT6          (),
      .DO               (),
      .DRDY             (),
      .PSDONE           (),
      .PSCLK            (1'b0),
      .PSEN             (1'b0),
      .PSINCDEC         (1'b0),
      .PWRDWN           (1'b0),
      .LOCKED           (mmcm_lckd),
      .CLKFBIN          (clkfb_clk),
      .CLKIN1           (clkint),
      .CLKIN2           (1'b0),
      .CLKINSEL         (1'b1),
      .DADDR            (7'h00),
      .DCLK             (1'b0),
      .DEN              (1'b0),
      .DI               (16'h0000),
      .DWE              (1'b0),
      .RST              (mmcm_reset)) ;

BUFG bufg_mmcm_200ref (
    .O(sys_clock_200ref), // 1-bit output: Clock output
    .I(txpllmmcm_200ref)  // 1-bit input: Clock input
);
BUFR #(
    .BUFR_DIVIDE("1"),
    .SIM_DEVICE("7SERIES"))
bufr_mmcm_fb (
    .I(txpllmmcm_clkfb),
    .CE(1'b1),
    .O(clkfb_clk),
    .CLR(1'b0)) ;

BUFIO bufio_mmcm_xn (
    .I(txpllmmcm_xn), 
    .O(txclk)) ;

BUFR #(
    .BUFR_DIVIDE("1"),
    .SIM_DEVICE("7SERIES"))
bufr_mmcm_x1 (
    .I(txpllmmcm_x1),
    .CE(1'b1),
    .O(pixel_clk),
    .CLR(1'b0)) ;

endmodule

你可能感兴趣的:(Xilinx,FPGA,Camera)