【ZYNQ入门】第四篇、HDMI发送端的构成原理

目录

第一部分、关于HDMI的知识

1、HDMI的含义

2、HDMI的结构

3、HDMI发送端的组成

第二部分、硬件部分

1、1920*1080@60Hz的VGA控制模块

1.1、VGA的输入时钟 

1.2、VGA控制器的代码

2、encode编码模块

2.1、编码目的

2.2、连接原理

2.3、encode代码        

3、SelectIO Interface Wizard IP模块

3.1、IP说明

3.2、IP调用方法

3.3、IP构成原理

3.4、OSERDESE2级联原理        

3.5、PLL时钟的问题

3.5.1、PLL报错

3.5.2、问题的原因

3.5.3、解决办法

3.6、OSERDESE2原语例化模板的位置

3.7、IP仿真验证

4、管脚约束

第三部分、总结

1、写在最后

2、工程文件


第一部分、关于HDMI的知识

1、HDMI的含义

  高清多媒体接口(High Definition Multimedia Interface,HDMI),是一种全数字化视频和声音发送接口,可以发送音频及视频信号

        HDMI向下兼容DVI(Digital Visual Interface,数字视频接口),DVI只能传输视频信号

2、HDMI的结构

        HDMI分为发送端和接收端,一般情况下,我们只用对接HDMI的发送端即可

3、HDMI发送端的组成

        如下图就是HDMI发送端模块的组成,发送端的程序执行流程如下:

首先,提供24bit的图片或者图像数据源(一般是从DDR内读取)。

随后,由VGA控制器(1920*1080@60Hz)提供RGB三原色以及行场同步信号

接着,由encode编码模块对RGB数据进行8bit转10bit的编码。目的:保证直流平衡,把 8 比特的数据从新映射为 10bit 数据, 防止连续的 0 和 1 出现导致 直流不平衡造成误码率升高。

其次,由OSERDESE2原语将编码后的10bit并行数据转换为单bit的串行数据。注意:串行时钟为并行时钟的10倍,但是为了降低时钟,采用双沿传输数据,因此串行时钟为并行时钟的5倍。

最后,再将单端的串行的单bit数据进行差分,调用原语OBUFDS 将串行的数据转成差分信号。

第二部分、硬件部分

        hdmi发送端部分的bolck design设计原理图如下

1、1920*1080@60Hz的VGA控制模块

        VGA控制器直接和AXI HP读模块结合,直接将AXI HP读出来的32bit数据处理为24bit(舍弃最高位的0)的三通道数据,R占八位,G占八位,B占八位。

1.1、VGA的输入时钟 

        1920*1080@60Hz的所需时钟频率为 148.5MHz。关于计算原理可以参考这篇文章:【FPGA入门】第七篇、FPGA实现VGA接口驱动_vga驱动-CSDN博客

        计算过程如下

              2000*1105*60 = 2200*1125*60 = 148,500,000Hz = 148.5MHz

        VESA 视频显示标准 VGA HDMI 不同分辨率刷新率对应的时钟频率和行列时序参数参考:

VESA-DMT-1.12.pdf (glenwing.github.io)

1.2、VGA控制器的代码

module	VGA_TIMING(
	input	wire		sclk,
	input	wire		rst_n,
	output	reg	[7:0]	po_vga_r,
	output	reg	[7:0]	po_vga_g,
	output	reg	[7:0]	po_vga_b,
	output	reg		po_de,
	output	reg		po_v_sync,
	output	reg		po_h_sync,
	input	wire	[31:0]	rgb_pixel,
	output	wire		rd_fifo_en
);
parameter	H_SYNC_TIME	=44;
parameter	H_BACK_PORCH	=148;
parameter	H_LEFT_BORDER	=0;
parameter	H_ACT_START	=H_SYNC_TIME + H_BACK_PORCH + H_LEFT_BORDER;
parameter	H_ACTIVE_TIME	=1920;
parameter	H_ACT_END	=H_ACT_START + H_ACTIVE_TIME;
parameter	H_TOTAL_TIME	=2200;
parameter	V_TOTAL_TIME	=1125;
parameter	V_SYNC_TIME	=5;
parameter	V_BACK_PORCH	=36;
parameter	V_TOP_BORDER	=0;
parameter	V_ACT_START	=V_SYNC_TIME + V_BACK_PORCH + V_TOP_BORDER;
parameter	V_ACTIVE_TIME	=1080;
parameter	V_ACT_END	=V_ACT_START + V_ACTIVE_TIME;


reg	[11:0]	hor_cnt			= 12'd0;//水平方向像素计数器
reg	[11:0]	ver_cnt 		= 12'd0;//垂直方向行计数器
reg		hor_end;
reg		hor_end_t;
reg		ver_end;
reg		h_sync_start_flag;
reg		h_sync_end_flag;
reg		h_active_flag;
reg	[11:0]	h_act_num;
reg		v_sync_start_flag;
reg		v_sync_end_flag;
reg		v_active_flag;
reg	[11:0]	v_act_num;

always	@(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		hor_cnt <= 'd0;
	else if(hor_end == 1'b1)
		hor_cnt <= 'd0;
	else 
		hor_cnt <= hor_cnt + 1'b1;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		hor_end <= 1'b0;
	else if(hor_cnt == H_TOTAL_TIME-2)
		hor_end <= 1'b1;
	else 
		hor_end <= 1'b0;
always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		hor_end_t<= 1'b0;
	else 
		hor_end_t<= hor_end;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		ver_cnt <= 'd0;
	else if(ver_end == 1'b1) //&& hor_end_t == 1'b1)
		ver_cnt <= 'd0;
	else  if(hor_end == 1'b1)
		ver_cnt <= ver_cnt + 1'b1;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		ver_end <= 1'b0;
	else if(ver_cnt == V_TOTAL_TIME -1 && hor_cnt == H_TOTAL_TIME-2)
		ver_end <= 1'b1;
	else
		ver_end <= 1'b0;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		h_sync_start_flag <= 1'b0;
	else if(hor_cnt == 'd0)
		h_sync_start_flag <= 1'b1;
	else
		h_sync_start_flag <= 1'b0;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		h_sync_end_flag <= 1'b0;
	else if(hor_cnt == H_SYNC_TIME)
		h_sync_end_flag <= 1'b1;
	else 
		h_sync_end_flag <= 1'b0;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		h_active_flag <= 1'b0;
	else if(hor_cnt == H_ACT_START)
		h_active_flag <= 1'b1;
	else if(hor_cnt == H_ACT_END)
		h_active_flag <= 1'b0;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		po_h_sync <= 1'b1;
	else if(h_sync_start_flag == 1'b1)
		po_h_sync <= 1'b1;
	else if(h_sync_end_flag == 1'b1)
		po_h_sync <= 1'b0;

always @*
	if(h_active_flag == 1'b1)
		h_act_num <= hor_cnt - H_ACTIVE_TIME;
	else
		h_act_num <='d0;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		v_sync_start_flag <= 1'b0;
	else if(ver_cnt == V_TOTAL_TIME -1 && hor_end_t == 1'b1)
		v_sync_start_flag <= 1'b1;
	else if(ver_cnt == 'd0 && hor_cnt == 'd0)
		v_sync_start_flag <= 1'b1;
	else 
		v_sync_start_flag <= 1'b0;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		v_sync_end_flag <= 1'b0;
	else if(ver_cnt == V_SYNC_TIME -1 && hor_end_t == 1'b1)
		v_sync_end_flag <= 1'b1;
	else 
		v_sync_end_flag <= 1'b0;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		v_active_flag <= 1'b0;
	else if(ver_cnt == V_ACT_START -1 && hor_end == 1'b1)
		v_active_flag <= 1'b1;
	else if(ver_cnt == V_ACT_END -1 && hor_end == 1'b1)
		v_active_flag <= 1'b0;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		po_v_sync <= 1'b1;
	else if(v_sync_start_flag == 1'b1)
		po_v_sync <= 1'b1;
	else if(v_sync_end_flag == 1'b1)
		po_v_sync <= 1'b0;

always @*
	if(v_active_flag == 1'b1 && h_active_flag == 1'b1)
		v_act_num <= ver_cnt - V_ACT_START;
	else 
		v_act_num <= 'd0;

always @(posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)
		po_de <=1'b0;
	else if(h_active_flag == 1'b1 && v_active_flag == 1'b1)
		po_de <= 1'b1;
	else
		po_de <= 1'b0;

assign	rd_fifo_en = h_active_flag & v_active_flag ;

always @ (posedge sclk or negedge rst_n)
	if(rst_n == 1'b0)begin
		po_vga_r <= 'd0;
		po_vga_g <= 'd0;
		po_vga_b <= 'd0;
	end
	else if(h_active_flag == 1'b1 && v_active_flag == 1'b1) begin
		po_vga_r <=rgb_pixel[23:16];
		po_vga_g <=rgb_pixel[15:8];
		po_vga_b <=rgb_pixel[7:0]; 
	end
	else begin
		po_vga_r <= 'd0;
		po_vga_g <= 'd0;
		po_vga_b <= 'd0;
	end
endmodule

2、encode编码模块

2.1、编码目的

        将八位的RGB三通道数据转换为10bit编码数据。

        编码目的:保证直流平衡,把 8 比特的数据从新映射为 10bit 数据, 防止连续的 0 和 1 出现导致直流不平衡造成误码率升高。

2.2、连接原理

        hdmi的连接架构如下,参考《高清晰度多媒体接口规范 1.4》。

        注意:只有blue通道的编码模块c0和c1需要连接,hsync连接c0,vsync连接c1。其它两个通道的c0和c1输入0。

2.3、encode代码        

`timescale 1 ps / 1ps

module encode (
  input            clkin,    // pixel clock input
  input            rstin,    // async. reset input (active high)
  input      [7:0] din,      // data inputs: expect registered
  input            c0,       // c0 input
  input            c1,       // c1 input
  input            de,       // de input
  output reg [9:0] dout      // data outputs
);

  
  // Counting number of 1s and 0s for each incoming pixel
  // component. Pipe line the result.
  // Register Data Input so it matches the pipe lined adder
  // output
  
  reg [3:0] n1d; //number of 1s in din
  reg [7:0] din_q;

  always @ (posedge clkin) begin
    n1d <= din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7];

    din_q <= din;
  end

  ///
  // Stage 1: 8 bit -> 9 bit
  // Refer to DVI 1.0 Specification, page 29, Figure 3-5
  ///
  wire decision1;

  assign decision1 = (n1d > 4'h4) | ((n1d == 4'h4) & (din_q[0] == 1'b0));
/*
  reg [8:0] q_m;
  always @ (posedge clkin) begin
    q_m[0] <=#1 din_q[0];
    q_m[1] <=#1 (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]);
    q_m[2] <=#1 (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]);
    q_m[3] <=#1 (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]);
    q_m[4] <=#1 (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]);
    q_m[5] <=#1 (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]);
    q_m[6] <=#1 (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]);
    q_m[7] <=#1 (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]);
    q_m[8] <=#1 (decision1) ? 1'b0 : 1'b1;
  end
*/
  wire [8:0] q_m;
  assign q_m[0] = din_q[0];
  assign q_m[1] = (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]);
  assign q_m[2] = (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]);
  assign q_m[3] = (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]);
  assign q_m[4] = (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]);
  assign q_m[5] = (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]);
  assign q_m[6] = (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]);
  assign q_m[7] = (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]);
  assign q_m[8] = (decision1) ? 1'b0 : 1'b1;

  /
  // Stage 2: 9 bit -> 10 bit
  // Refer to DVI 1.0 Specification, page 29, Figure 3-5
  /
  reg [3:0] n1q_m, n0q_m; // number of 1s and 0s for q_m
  always @ (posedge clkin) begin
    n1q_m  <= q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7];
    n0q_m  <= 4'h8 - (q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]);
  end

  parameter CTRLTOKEN0 = 10'b1101010100;
  parameter CTRLTOKEN1 = 10'b0010101011;
  parameter CTRLTOKEN2 = 10'b0101010100;
  parameter CTRLTOKEN3 = 10'b1010101011;

  reg [4:0] cnt; //disparity counter, MSB is the sign bit
  wire decision2, decision3;

  assign decision2 = (cnt == 5'h0) | (n1q_m == n0q_m);
  /
  // [(cnt > 0) and (N1q_m > N0q_m)] or [(cnt < 0) and (N0q_m > N1q_m)]
  /
  assign decision3 = (~cnt[4] & (n1q_m > n0q_m)) | (cnt[4] & (n0q_m > n1q_m));

  
  // pipe line alignment
  
  reg       de_q, de_reg;
  reg       c0_q, c1_q;
  reg       c0_reg, c1_reg;
  reg [8:0] q_m_reg;

  always @ (posedge clkin) begin
    de_q    <= de;
    de_reg  <= de_q;
    
    c0_q    <= c0;
    c0_reg  <= c0_q;
    c1_q    <= c1;
    c1_reg  <= c1_q;

    q_m_reg <= q_m;
  end

  ///
  // 10-bit out
  // disparity counter
  ///
  always @ (posedge clkin or posedge rstin) begin
    if(rstin) begin
      dout <= 10'h0;
      cnt <= 5'h0;
    end else begin
      if (de_reg) begin
        if(decision2) begin
          dout[9]   <= ~q_m_reg[8]; 
          dout[8]   <= q_m_reg[8]; 
          dout[7:0] <= (q_m_reg[8]) ? q_m_reg[7:0] : ~q_m_reg[7:0];

          cnt <=#1 (~q_m_reg[8]) ? (cnt + n0q_m - n1q_m) : (cnt + n1q_m - n0q_m);
        end else begin
          if(decision3) begin
            dout[9]   <= 1'b1;
            dout[8]   <= q_m_reg[8];
            dout[7:0] <= ~q_m_reg[7:0];

            cnt <=#1 cnt + {q_m_reg[8], 1'b0} + (n0q_m - n1q_m);
          end else begin
            dout[9]   <= 1'b0;
            dout[8]   <= q_m_reg[8];
            dout[7:0] <= q_m_reg[7:0];

            cnt <= cnt - {~q_m_reg[8], 1'b0} + (n1q_m - n0q_m);
          end
        end
      end else begin
        case ({c1_reg, c0_reg})
          2'b00:   dout <= CTRLTOKEN0;
          2'b01:   dout <= CTRLTOKEN1;
          2'b10:   dout <= CTRLTOKEN2;
          default: dout <= CTRLTOKEN3;
        endcase

        cnt <= 5'h0;
      end
    end
  end
  
endmodule

3、SelectIO Interface Wizard IP模块

3.1、IP说明

        这个IP实际上是由原语OSERDESR2并转串模块OBUFDS单端转差分模块构成的。也就是这个IP包含了这两种原语。

        大家可以参考这个博主的这篇博客,写的很详细:Xilinx中oserdes的原语及IP的使用

3.2、IP调用方法

第一步、配置第一个界面,输入/输出数据位宽,电平标准等

第二步、配置第二个界面

第三步、配置第三个界面,保持默认

 第四步、最后一个界面

        注意:clk_in端口输入的是高速时钟(5倍并行时钟),clk_div_in为慢速的并行时钟。

3.3、IP构成原理

        在IP生成好之后,实际的代码如下

`timescale 1ps/1ps

module design_1_selectio_wiz_0_3_selectio_wiz
   // width of the data for the system
 #(parameter SYS_W = 1,
   // width of the data for the device
   parameter DEV_W = 10)
 (
  // From the device out to the system
  input  [DEV_W-1:0] data_out_from_device,
  output [SYS_W-1:0] data_out_to_pins_p,
  output [SYS_W-1:0] data_out_to_pins_n,
  input              clk_in,        // Fast clock input from PLL/MMCM
  input              clk_div_in,    // Slow clock input from PLL/MMCM
  input              io_reset);
  localparam         num_serial_bits = DEV_W/SYS_W;
  wire clock_enable = 1'b1;
  // Signal declarations
  ------------------------------
  // Before the buffer
  wire   [SYS_W-1:0] data_out_to_pins_int;
  // Between the delay and serdes
  wire   [SYS_W-1:0] data_out_to_pins_predelay;
  // Array to use intermediately from the serdes to the internal
  //  devices. bus "0" is the leftmost bus
  wire [SYS_W-1:0]  oserdes_d[0:13];   // fills in starting with 13
  // Create the clock logic


  // We have multiple bits- step over every bit, instantiating the required elements
  genvar pin_count;
  genvar slice_count;
  generate for (pin_count = 0; pin_count < SYS_W; pin_count = pin_count + 1) begin: pins
    // Instantiate the buffers
    ------------------------------
    // Instantiate a buffer for every bit of the data bus
    OBUFDS
      #(.IOSTANDARD ("TMDS_33"))
     obufds_inst
       (.O          (data_out_to_pins_p  [pin_count]),
        .OB         (data_out_to_pins_n  [pin_count]),
        .I          (data_out_to_pins_int[pin_count]));

    // Pass through the delay
    -------------------------------
   assign data_out_to_pins_int[pin_count]    = data_out_to_pins_predelay[pin_count];
 
     // Instantiate the serdes primitive
     ------------------------------

     wire [SYS_W-1:0]  ocascade_sm_d;
     wire [SYS_W-1:0]  ocascade_sm_t;
     // declare the oserdes
     OSERDESE2
       # (
         .DATA_RATE_OQ   ("DDR"),
         .DATA_RATE_TQ   ("SDR"),
         .DATA_WIDTH     (10),
         .TRISTATE_WIDTH (1),
         .SERDES_MODE    ("MASTER"))
       oserdese2_master (
         .D1             (oserdes_d[13][pin_count]),
         .D2             (oserdes_d[12][pin_count]),
         .D3             (oserdes_d[11][pin_count]),
         .D4             (oserdes_d[10][pin_count]),
         .D5             (oserdes_d[9][pin_count]),
         .D6             (oserdes_d[8][pin_count]),
         .D7             (oserdes_d[7][pin_count]),
         .D8             (oserdes_d[6][pin_count]),
         .T1             (1'b0),
         .T2             (1'b0),
         .T3             (1'b0),
         .T4             (1'b0),
         .SHIFTIN1       (ocascade_sm_d[pin_count]),
         .SHIFTIN2       (ocascade_sm_t[pin_count]),
         .SHIFTOUT1      (),
         .SHIFTOUT2      (),
         .OCE            (clock_enable),
         .CLK            (clk_in),
         .CLKDIV         (clk_div_in),
         .OQ             (data_out_to_pins_predelay[pin_count]),
         .TQ             (),
         .OFB            (),
         .TFB            (),
         .TBYTEIN        (1'b0),
         .TBYTEOUT       (),
         .TCE            (1'b0),
         .RST            (io_reset));

     OSERDESE2
     #  (
         .DATA_RATE_OQ   ("DDR"),
         .DATA_RATE_TQ   ("SDR"),
         .DATA_WIDTH     (10),
         .TRISTATE_WIDTH (1),
         .SERDES_MODE    ("SLAVE"))
       oserdese2_slave (
         .D1             (1'b0), 
         .D2             (1'b0),
         .D3             (oserdes_d[5][pin_count]),
         .D4             (oserdes_d[4][pin_count]),
         .D5             (oserdes_d[3][pin_count]),
         .D6             (oserdes_d[2][pin_count]),
         .D7             (oserdes_d[1][pin_count]),
         .D8             (oserdes_d[0][pin_count]),
         .T1             (1'b0),
         .T2             (1'b0),
         .T3             (1'b0),
         .T4             (1'b0),
         .SHIFTOUT1       (ocascade_sm_d[pin_count]),
         .SHIFTOUT2       (ocascade_sm_t[pin_count]),
         .SHIFTIN1        (1'b0),
         .SHIFTIN2        (1'b0),
         .OCE            (clock_enable),
         .CLK            (clk_in),
         .CLKDIV         (clk_div_in),
         .OQ             (), //data_out_to_pins_predelay[pin_count]),
         .TQ             (),
         .OFB            (),
         .TFB            (),
         .TBYTEIN        (1'b0),
         .TBYTEOUT       (),
         .TCE            (1'b0),
         .RST            (io_reset));
     // Concatenate the serdes outputs together. Keep the timesliced
     //   bits together, and placing the earliest bits on the right
     //   ie, if data comes in 0, 1, 2, 3, 4, 5, 6, 7, ...
     //       the output will be 3210, 7654, ...
     ---------------------------------------------------------
     for (slice_count = 0; slice_count < num_serial_bits; slice_count = slice_count + 1) begin: out_slices
        // This places the first data in time on the right
        assign oserdes_d[14-slice_count-1] =
           data_out_from_device[slice_count];
        // To place the first data in time on the left, use the
        //   following code, instead
        // assign oserdes_d[slice_count] =
        //    data_out_from_device[slice_count];
     end
  end
  endgenerate

endmodule

3.4、OSERDESE2级联原理        

        原语中的OSERDESE2单个模块只支持8:1的并转串,不支持10:1的并转串。

        因此这里需要拓展的并转串,因此需要级联一个OSERDESE2模块。结构图如下:

3.5、PLL时钟的问题

        并转串为10:1的时候,那么需要准备两个时钟,一个时钟为并行数据的时钟,另一个是串行数据时钟(正常情况下为并行时钟的10倍,但是频率太高。系统内默认双沿采样,因此时钟变为原来的5倍)。

        因此串行时钟 = 148.5MHz * 5 = 742.5MHz。

        而PLL在产生742.5MHz的时钟会爆红,因为超出了最高时钟频率范围,如下图所示。

        但是可以正常使用上,因为是给出去的管脚时钟,所以没有关系。

3.5.1、PLL报错

其次,我的编译器在生成bit流文件的时候,PLL会报错如下

3.5.2、问题的原因

        当把FPGA中的时钟(比如DCM和PLL输出的时钟)输出到普通I/O口时,不能直接输出,要经过BUFGODDR2,但是需要注意的是如果我们调用的是IP核那么不需要调用BUFG的原语,因为IP核已经包括了BUFG,如果这个IP没有包含BUFG,那就需要BUFG。

        感觉这玩意就是让时钟更稳定。

        具体可参考这篇博客:FPGA-全局时钟缓冲IBUFG BUFG IBUFGDS ODDR2

3.5.3、解决办法

        将PLL的时钟源改为Global buffer全局时钟缓冲。

3.6、OSERDESE2原语例化模板的位置

        OSERDESE2原语也可以单独例化,其中例化的模板路径如下图

3.7、IP仿真验证

        验证这个IP,如何把串行10bit11111_00000,变为差分输出的时钟,因为分辨率为1920*1080@60Hz的VGA的时钟为178.5MHz,因此串行10bit11111_00000形成的时钟也是178.5MHz。

        仿真代码如下

`timescale 1ns / 1ps

module sim;
reg clk;
reg rst;
reg [9:0] din;
wire do_p;
wire do_n;

initial begin
    clk = 0;
    rst = 1;
    din = 10'b0;
    #100;
    rst = 0;
    din = 10'b11111_00000;
end

always #5 clk = ~clk;

design_1_selectio_wiz_0_3_selectio_wiz inst_test (
    .clk(clk), 
    .rst(rst), 
    .din(din),
    .do_p(do_p), 
    .do_n(do_n)
     );


endmodule

        仿真波形如下

        因为oserdese2的串行时钟是并行时钟的5倍,10'b11111_00000在串行时钟下就变成了并行时钟。

4、管脚约束

        管脚约束代码如下,关于差分端口,只需要约束一个端口,后面一个端口会自动被约束。

set_property PACKAGE_PIN E19 [get_ports HDMI_OUT_EN]
set_property IOSTANDARD LVCMOS33 [get_ports HDMI_OUT_EN]
set_property PACKAGE_PIN K17 [get_ports HDMI_CLK_P]
set_property IOSTANDARD TMDS_33 [get_ports HDMI_CLK_P]
set_property PACKAGE_PIN F19 [get_ports HDMI_D0_P]
set_property IOSTANDARD TMDS_33 [get_ports HDMI_D0_P]
set_property IOSTANDARD TMDS_33 [get_ports HDMI_D1_P]
set_property PACKAGE_PIN J18 [get_ports HDMI_D1_P]
set_property PACKAGE_PIN H16 [get_ports HDMI_D2_P]
set_property IOSTANDARD TMDS_33 [get_ports HDMI_D2_P]

        其次,这里HDMI_OUT_EN, 我的开发板原理图中默认关闭的,因此在block design中要给一个常量1,保证hdmi处于工作状态。

第三部分、总结

1、写在最后

        该篇文章介绍了HDMI发送端的构成原理、接着是Block design的设计原理以及内部模块构成原理。

       主要是我调试过程中的笔记,希望能够给你提供一定的思路。

2、工程文件

        这篇文章是为了下面这篇文章的内容做铺垫,关于工程下载链接以及实验现象,请参考这篇文章【ZYNQ实验】第一篇、ZYNQ驱动HDMI显示图片-CSDN博客

  • 25
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: 米联客 2022 版 Zynq SoC SDK 入门篇介绍了如何使用米联客提供的软件开发工具包(SDK)来开发和编程Zynq SoC芯片。 米联客是一个知名的半导体公司,其产品包括Zynq SoC芯片。Zynq SoC芯片具有可编程逻辑和处理器的组合,能够提供高性能和可定制化的解决方案。 在2022版Zynq SoC SDK入门篇中,首先介绍了Zynq SoC的基本架构和主要特性,包括可编程逻辑部分(FPGA)和处理器部分(ARM Cortex-A系列)。读者可以了解Zynq SoC的硬件组成和功能。 接下来,SDK入门篇介绍了如何设置开发环境并安装所需软件和驱动程序。读者可以了解如何配置开发工具和开发板,以便能够开始开发和编程。 然后,SDK入门篇介绍了基本的开发流程和工具。读者可以学习如何创建一个新的项目,并使用SDK提供的工具和函数库来编写代码。还介绍了如何调试和测试代码,并了解使用SDK的调试器和仿真器进行硬件验证。 最后,SDK入门篇提供了一些示例项目和案例,以帮助读者更好地理解和应用所学知识。这些示例项目涵盖了不同的应用场景和功能,读者可以根据自己的需求选择合适的示例项目进行学习和实践。 通过米联客 2022 版 Zynq SoC SDK 入门篇,读者可以快速入门并掌握Zynq SoC的开发和编程技巧,从而能够在实际项目中应用和开发基于Zynq SoC的解决方案。这将有助于提高工作效率和产品质量,进一步推动半导体和嵌入式系统领域的发展。 ### 回答2: Zynq SoC 是赛灵思公司基于 ARM Cortex-A9 核心和 FPGA 技术的一种可编程逻辑器件。相比于传统的单片机,Zynq SoC 具备更强大的计算和处理能力,适用于高性能嵌入式系统开发。 米联客 2022 版 Zynq SoC SDK 入门篇是一本介绍如何使用 Zynq SoC 开发环境的入门教程。该教程主要涵盖以下几个方面: 1. SDK 环境搭建:教程将指导读者如何安装 Zynq SoC SDK,并配置相关环境变量和工具链。读者将学习如何建立与目标板的连接,并进行基本的硬件配置。 2. 开发流程:教程将详细介绍用于 Zynq SoC 开发的基本流程,包括创建项目、编写应用程序代码、构建和调试等。读者将逐步了解 Zynq SoC 开发的基本步骤和工具的使用方法。 3. API 应用:教程将介绍 Zynq SoC SDK 提供的各种 API 接口,包括处理器驱动、硬件外设驱动等。读者将学习如何使用这些 API 接口来访问硬件资源,实现所需功能。 4. 示例项目:教程将提供一些简单的示例项目,如 LED 灯控制、按键输入等,帮助读者快速上手和理解 Zynq SoC 的开发流程和应用编程接口。 通过学习米联客 2022 版 Zynq SoC SDK 入门篇,读者将掌握 Zynq SoC 开发环境的搭建和基本使用方法,能够开始进行自己的项目开发。同时,教程也为读者提供了继续深入学习和探索 Zynq SoC 开发的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大屁桃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值