目录
3、SelectIO Interface Wizard IP模块
第一部分、关于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驱动-优快云博客
计算过程如下
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口时,不能直接输出,要经过BUFG和ODDR2,但是需要注意的是如果我们调用的是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显示图片-优快云博客