21. 基于以太网传输的图片显示(HDMI)

在“以太网数据环回实验”章节,我们设计并实现了以太网数据收发器,通过实验实现了PC端与板卡的数据交互。在本章节,我们使用已经通过验证的以太网数据收发器,实现基于以太网传输的HDMI图片显示。

21.1. 理论学习

本章节涉及到的PLL时钟IP核、SDRAM、HDMI以及以太网的相关知识在前面章节均有详细讲解,此处不再赘述。读者若有遗忘,请回顾相关章节进行学习巩固。

21.2. 实战演练

21.2.1. 实验目标

通过实验设计并实现基于以太网传输的HDMI图片显示。在PC端通过网络调试助手向板卡发送图片数据源文件,FPGA通过以太网数据收发器接收并提取图片数据,将图片数据暂存到SDRAM中,VGA驱动模块读取暂存在SDRAM的图片数据,HDMI驱动模块将VGA时序转换为HDMI时序并显示在HDMI显示器上,分 辨率为640*480。

21.2.2. 硬件资源

硬件资源部分,读者可参阅“SDRAM读写控制器的设计与验证”“HDMI显示器驱动设计与验证”和“以太网数据回环实验”章节的硬件资源小节。

21.3. 程序设计

21.3.1. 整体说明

实验目标已经明确,硬件资源也已经介绍完毕,我们开始程序设计部分的讲解。我们先来对实验工程进行一个整体说明,让读者了解整个实验工程的框架结构。实验工程整体框图,具体见图 71‑1;子功能模块简介,具体见表格 71‑1。

Enetpi002

图 71‑1 实验工程整体框图

表格 71‑1 子功能模块功能描述

模块名称

功能描述

clk_gen

时钟信号生成模块(IP核)

data32_to_data16

数据位宽转换模块

eth_udp_mii

以太网数据收发器

rmii_to_mii

RMII接口转MII接口模块

vga_ctrl

VGA显示驱动模块

hdmi_ctrl

HDMI显示驱动模块

sdram_top

SDRAM数据读写控制器

eth_hdmi_pic

顶层模块

由上述图表可知,实验工程包含6个子功能模块:时钟信号生成模块(clk_gen(IP核))、数据位宽转换模块(data32_to_data16)、RMII接口转MII接口模块(rmii_to_mii)、以太网数据收发器 (eth_udp_mii)、VGA显示驱动模块(vga_ctrl)、HDMI显示驱动模块(hdmi_ctrl)、SDRAM数据读写控制器(sdram_top)和顶层模块(eth_hdmi_pic)。

时钟信号生成模块(clk_gen),为实验工程各模块提供工作时钟,调用IP核生成;数据位宽转换模块(data32_to_data16),将自以太网数据包中读出的32位位宽数据转换为可写入SDRAM的16位位宽数据;RMII接口转MII接口模块(rmii_to_mii),实现RMII接口到MII接口的 转换;以太网数据收发器(eth_udp_mii),实现以太网数据收发操作;VGA显示驱动模块(vga_ctrl),按照VGA时序要求自SDRAM中读取图片数据;HDMI驱动模块将VGA时序图片转换为HDMI时序并显示;SDRAM数据读写控制器(sdram_top),暂存自以太网数据包中读出的图片数据 ;顶层模块(eth_hdmi_pic),例化子功能模块,连接各自对应信号。

这一部分我们对实验工程整体做了简要介绍,工程涉及的部分子功能模块在前面章节均已有详细介绍,下面我们值简述一下顶层模块。

21.3.1.1. 顶层模块

模块框图

顶层模块作用较为简单,内部实例化各子功能模块,连接子模块对应信号;外部输入输出使能信号和以太网数据。在此我们只对顶层模块的整体框图和参考代码做一下介绍,无需绘制波形图。模块框图和输入输出信号简介,具体见图 71‑2、表格 71‑2。

Enetpi003

图 71‑2 顶层模块框图

表格 71‑2 输入输出信号功能描述

信号

位宽

类型

功能描述

sys_clk

1Bit

Input

板卡晶振输入时钟,50MHz

sys_rst_n

1Bit

Input

系统复位,低电平有效

eth_rx_clk

1Bit

Input

PHY芯片接收数据时钟信号

eth_rxdv

1Bit

Input

PHY芯片输入数据有效信号

eth_rx_data

4Bit

Input

PHY芯片输入数据

sdram_clk

1Bit

Output

SDRAM芯片时钟

sdram_cke

1Bit

Output

SDRAM时钟有效信号

sdram_cs_n

1Bit

Output

SDRAM片选信号

sdram_ras_n

1Bit

Output

SDRAM行地址选通信号

sdram_cas_n

1Bit

Output

SDRAM列地址选通信号

sdram_we_n

1Bit

Output

SDRAM写允许信号

sdram_ba

2Bit

Output

SDRAM的L-Bank地址线

sdram_addr

13Bit

Output

SDRAM地址总线

sdram_dqm

2Bit

Output

SDRAM数据掩码

sdram_dq

16Bit

Output

SDRAM数据总线

eth_tx_en

1Bit

Output

PHY芯片输出数据有效信号

eth_rst_n

1Bit

Output

PHY芯片复位信号,低电平有效

ddc_scl

1Bit

Output

DDC串行时钟信号

ddc_sda

1Bit

Output

DDC串行数据信号

tmds_clk_p

1Bit

Output

HDMI差分时钟信号

tmds_clk_n

1Bit

Output

HDMI差分时钟信号

tmds_data_p

3Bit

Output

HDMI图像差分信号

tmds_data_n

3Bit

Output

HDMI图像差分信号

代码编写

顶层模块参考代码,具体见代码清单 71‑1。

代码清单 71‑1 顶层模块参考代码(eth_hdmi_pic.v)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
module eth_hdmi_pic
(
input wire sys_clk , //系统时钟
input wire sys_rst_n , //复位信号,低电平有效
//Ethernet
input wire eth_rx_clk , //PHY芯片接收数据时钟信号
input wire eth_rxdv_r , //PHY芯片输入数据有效信号
input wire [1:0] eth_rx_data_r, //PHY芯片输入数据
output wire eth_tx_en , //PHY芯片输出数据有效信号
output wire eth_rst_n , //PHY芯片复位信号,低电平有效
//SDRAM
output wire sdram_clk , //SDRAM芯片时钟
output wire sdram_cke , //SDRAM时钟有效信号
output wire sdram_cs_n , //SDRAM片选信号
output wire sdram_ras_n , //SDRAM行地址选通信号
output wire sdram_cas_n , //SDRAM列地址选通信号
output wire sdram_we_n , //SDRAM写允许信号
output wire [1:0] sdram_ba , //SDRAM的L-Bank地址线
output wire [12:0] sdram_addr , //SDRAM地址总线
output wire [1:0] sdram_dqm , //SDRAM数据掩码
inout wire [15:0] sdram_dq , //SDRAM数据总线

//hdmi
output wire ddc_scl ,
output wire ddc_sda ,
output wire tmds_clk_p ,
output wire tmds_clk_n , //HDMI时钟差分信号
output wire [2:0] tmds_data_p ,
output wire [2:0] tmds_data_n //HDMI图像差分信号
);

////
//\* Parameter and Internal Signal \//
////
//parameter define
parameter BOARD_MAC = 48'hFF_FF_FF_FF_FF_FF ; //板卡MAC地址
parameter BOARD_IP = 32'hFF_FF_FF_FF ; //板卡IP地址
parameter BOARD_PORT = 16'd1234 ; //板卡端口号
parameter PC_MAC = 48'hE0_D5_5E_4A_DB_2D ; //PC机MAC地址
parameter PC_IP = 32'hC0_A8_64_02 ; //PC机IP地址
parameter PC_PORT = 16'd1234 ; //PC机端口号

/\* parameter BOARD_MAC = 48'h12_34_56_78_9a_bc ; //板卡MAC地址
parameter BOARD_IP = 32'hA9_FE_01_17 ; //板卡IP地址
parameter BOARD_PORT = 16'd1234 ; //板卡端口号
parameter PC_MAC = 48'hE0_D5_5E_4A_DB_2D ; //PC机MAC地址
parameter PC_IP = 32'hA9_FE_F2_37 ; //PC机IP地址
parameter PC_PORT = 16'd1234 ; //PC机端口号 \*/

parameter H_VALID = 24'd640; //行有效数据
parameter V_VALID = 24'd480; //列有效数据

//wire define
wire clk_25m ; //25MHz
wire clk_100m ; //100MHz
wire clk_100m_shift ; //100MHz,做相位偏移处理
wire clk_125m ;
wire locked ; //时钟输出有效
wire rst_n ; //系统复位信号
wire sdram_init_done ; //SDRAM完成初始化
wire rec_en_in ; //UDP接收有效数据使能
wire [31:0] rec_data_in ; //UDP接收有效数据
wire wr_en ; //SDRAM写端口写使能
wire [15:0] wr_data ; //SDRAM写端口写数据
wire rd_en ; //SDRAM读端口读使能
wire [15:0] rd_data ; //SDRAM读端口读数据
wire hsync ; //输出行同步信号
wire vsync ; //输出场同步信号
wire [15:0] vga_rgb ; //输出像素点色彩信息
wire rgb_valid ;
wire eth_rxdv ;
wire [3:0] eth_rx_data ;

//reg define
reg mii_clk ;

////
//\* Main Code \//
////
//rst_n:/系统复位信号
assign rst_n = sys_rst_n & locked;
assign ddc_scl = 1'b1;
assign ddc_sda = 1'b1;

always@(negedge eth_rx_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
mii_clk <= 1'b1;
else
mii_clk <= ~mii_clk;

////
//\* Instantiation \//
////
//------------ clk_gen_inst -------------
clk_gen clk_gen_inst
(
.inclk0 (sys_clk ), //输入时钟
.areset (~sys_rst_n ), //复位信号,高有效

.c0 (clk_25m ), //输出25MHz时钟
.c1 (clk_100m ), //输出100MHz时钟
.c2 (clk_100m_shift ), //输出100MHz时钟,相位偏移
.c3 (clk_125m ),
.locked (locked ) //时钟信号有效标志
);

//------------ rmii_to_mii_inst -------------
rmii_to_mii rmii_to_mii_inst
(
.eth_rmii_clk(eth_rx_clk ),
.eth_mii_clk (mii_clk ),
.sys_rst_n (sys_rst_n ),
.rx_dv (eth_rxdv_r ),
.rx_data (eth_rx_data_r ),

.eth_rx_dv (eth_rxdv ),
.eth_rx_data (eth_rx_data )
);

//------------ eth_udp_inst -------------
eth_udp_mii
#(
.BOARD_MAC (BOARD_MAC ), //板卡MAC地址
.BOARD_IP (BOARD_IP ), //板卡IP地址
.BOARD_PORT (BOARD_PORT ), //板卡端口号
.PC_MAC (PC_MAC ), //PC机MAC地址
.PC_IP (PC_IP ), //PC机IP地址
.PC_PORT (PC_PORT ) //PC机端口号
)
eth_udp_mii_inst
(
.eth_rx_clk (mii_clk ), //PHY芯片接收数据时钟信号
.sys_rst_n (rst_n ), //复位信号,低电平有效
.eth_rxdv (eth_rxdv ), //PHY芯片输入数据有效信号
.eth_rx_data (eth_rx_data ), //PHY芯片输入数据
.eth_tx_clk ( ), //PHY芯片发送数据时钟信号
.send_en ( ), //开始发送信号
.send_data ( ), //发送数据
.send_data_num ( ), //发送有效数据字节数

.send_end ( ), //单包数据发送完成信号
.read_data_req ( ), //读数据请求信号
.rec_end ( ), //单包数据接收完成信号
.rec_en (rec_en_in ), //接收数据使能信号
.rec_data (rec_data_in ), //接收数据
.rec_data_num ( ), //接收有效数据字节数
.eth_tx_en (eth_tx_en ), //PHY芯片输出数据有效信号
.eth_tx_data ( ), //PHY芯片输出数据
.eth_rst_n (eth_rst_n ) //PHY芯片复位信号,低电平有效
);

//------------ data32_to_data16_inst -------------
data32_to_data16 data32_to_data16_inst
(
.sys_clk (mii_clk ), //系统时钟
.sys_rst_n (rst_n ), //复位信号,低有效
.rec_en_in (rec_en_in ), //输入32位数据使能信号
.rec_data_in (rec_data_in), //输入32位数据

.rec_en_out (wr_en ), //输出16位数据使能信号
.rec_data_out (wr_data ) //输出16位数据
);

//------------- sdram_top_inst -------------
sdram_top sdram_top_inst
(
.sys_clk (clk_100m ), //sdram 控制器参考时钟
.clk_out (clk_100m_shift ), //用于输出的相位偏移时钟
.sys_rst_n (rst_n ), //系统复位
//用户写端口
.wr_fifo_wr_clk (mii_clk ), //写端口FIFO: 写时钟
.wr_fifo_wr_req (wr_en ), //写端口FIFO: 写使能
.wr_fifo_wr_data (wr_data ), //写端口FIFO: 写数据
.sdram_wr_b_addr (24'd0 ), //写SDRAM的起始地址
.sdram_wr_e_addr (H_VALID*V_VALID), //写SDRAM的结束地址
.wr_burst_len (10'd512 ), //写SDRAM时的数据突发长度
.wr_rst (~rst_n ), //写端口复位: 复位写地址,清空写FIFO
//用户读端口
.rd_fifo_rd_clk (clk_25m ), //读端口FIFO: 读时钟
.rd_fifo_rd_req (rd_en ), //读端口FIFO: 读使能
.rd_fifo_rd_data (rd_data ), //读端口FIFO: 读数据
.sdram_rd_b_addr (24'd0 ), //读SDRAM的起始地址
.sdram_rd_e_addr (H_VALID*V_VALID), //读SDRAM的结束地址
.rd_burst_len (10'd512 ), //从SDRAM中读数据时的突发长度
.rd_fifo_num ( ), //读fifo中的数据量
.rd_rst (~rst_n ), //读端口复位: 复位读地址,清空读FIFO
//用户控制端口
.read_valid (1'b1 ), //SDRAM 读使能
.pingpang_en (1'b0 ), //SDRAM 乒乓操作使能
.init_end (sdram_init_done), //SDRAM 初始化完成标志
//SDRAM 芯片接口
.sdram_clk (sdram_clk ), //SDRAM 芯片时钟
.sdram_cke (sdram_cke ), //SDRAM 时钟有效
.sdram_cs_n (sdram_cs_n ), //SDRAM 片选
.sdram_ras_n (sdram_ras_n ), //SDRAM 行有效
.sdram_cas_n (sdram_cas_n ), //SDRAM 列有效
.sdram_we_n (sdram_we_n ), //SDRAM 写有效
.sdram_ba (sdram_ba ), //SDRAM Bank地址
.sdram_addr (sdram_addr ), //SDRAM 行/列地址
.sdram_dq (sdram_dq ), //SDRAM 数据
.sdram_dqm (sdram_dqm ) //SDRAM 数据掩码
);


//------------ vga_ctrl_inst -------------
vga_ctrl vga_ctrl_inst
(
.vga_clk (clk_25m ), //输入工作时钟,频率25MHz
.sys_rst_n (sdram_init_done ), //输入复位信号,低电平有效
.pix_data (rd_data ), //输入待显示像素信息

.rgb_valid (rgb_valid ), //VGA有效显示区域
.pix_data_req(rd_en ), //VGA数据请求信号
.hsync (hsync ), //输出行同步信号
.vsync (vsync ), //输出场同步信号
.rgb (vga_rgb ) //输出像素信息
);

//------------- hdmi_ctrl_inst -------------
hdmi_ctrl hdmi_ctrl_inst
(
.clk_1x (clk_25m ), //输入系统时钟
.clk_5x (clk_125m ), //输入5倍系统时钟
.sys_rst_n (rst_n ), //复位信号,低有效
.rgb_blue ({vga_rgb[4:0],3'b0} ), //蓝色分量
.rgb_green ({vga_rgb[10:5],2'b0} ), //绿色分量
.rgb_red ({vga_rgb[15:11],3'b0} ), //红色分量
.hsync (hsync ), //行同步信号
.vsync (vsync ), //场同步信号
.de (rgb_valid ), //使能信号
.hdmi_clk_p (tmds_clk_p ),
.hdmi_clk_n (tmds_clk_n ), //时钟差分信号
.hdmi_r_p (tmds_data_p[2] ),
.hdmi_r_n (tmds_data_n[2] ), //红色分量差分信号
.hdmi_g_p (tmds_data_p[1] ),
.hdmi_g_n (tmds_data_n[1] ), //绿色分量差分信号
.hdmi_b_p (tmds_data_p[0] ),
.hdmi_b_n (tmds_data_n[0] ) //蓝色分量差分信号
);

endmodule

21.4. 上板调试

21.4.1. 引脚约束

仿真验证通过后,准备上板验证,上板验证之前先要进行引脚约束。工程中各输入输出信号与开发板引脚对应关系如表格 55‑3所示。

表格 71‑3 引脚分配表

信号名

信号类型

对应引脚

备注

eth_rx_clk

Input

C3

PHY芯片传入以太网时钟,频率50MHz

sys_clk

Input

E1

板卡晶振传入时钟,频率50MHz

sys_rst_n

Input

M15

复位信号,低有效

eth_rxdv_r

Input

D3

输入数据有效信号

eth_rx_data_r[1]

Input

D8

输入数据

eth_rx_data_r[0]

Input

A4

输入数据

eth_tx_en

Output

D5

输出数据使能信号

eth_rst_n

Output

B3

PHY复位信号

sdram_clk

Output

R4

SDRAM芯片时钟

sdram_cke

Output

R9

SDRAM时钟有效信号

sdram_cs_n

Output

R12

SDRAM片选信号

sdram_cas_n

Output

R10

SDRAM列地址选通脉冲

sdram_ras_n

Output

R11

SDRAM行地址选通脉冲

sdram_we_n

Output

L9

SDRAM写允许位

sdram_ba[0]

Output

R13

SDRAM的L-Bank地址线

sdram_ba[1]

Output

R14

SDRAM的L-Bank地址线

sdram_addr[0]

Output

P11

SDRAM地址总线

sdram_addr[1]

Output

P14

SDRAM地址总线

sdram_addr[2]

Output

N9

SDRAM地址总线

sdram_addr[3]

Output

N11

SDRAM地址总线

sdram_addr[4]

Output

T14

SDRAM地址总线

sdram_addr[5]

Output

T13

SDRAM地址总线

sdram_addr[6]

Output

T12

SDRAM地址总线

sdram_addr[7]

Output

T11

SDRAM地址总线

sdram_addr[8]

Output

T10

SDRAM地址总线

sdram_addr[9]

Output

P9

SDRAM地址总线

sdram_addr[10]

Output

T15

SDRAM地址总线

sdram_addr[11]

Output

N12

SDRAM地址总线

sdram_addr[12]

Output

M11

SDRAM地址总线

sdram_dqm[0]

Output

M10

SDRAM数据掩码

sdram_dqm[1]

Output

M9

SDRAM数据掩码

sdram_dq[0]

Output

R3

SDRAM数据总线

sdram_dq[1]

Output

T9

SDRAM数据总线

sdram_dq[2]

Output

R5

SDRAM数据总线

sdram_dq[3]

Output

R6

SDRAM数据总线

sdram_dq[4]

Output

R7

SDRAM数据总线

sdram_dq[5]

Output

M8

SDRAM数据总线

sdram_dq[6]

Output

R8

SDRAM数据总线

sdram_dq[7]

Output

N8

SDRAM数据总线

sdram_dq[8]

Output

P8

SDRAM数据总线

sdram_dq[9]

Output

T8

SDRAM数据总线

sdram_dq[10]

Output

T7

SDRAM数据总线

sdram_dq[11]

Output

T6

SDRAM数据总线

sdram_dq[12]

Output

T5

SDRAM数据总线

sdram_dq[13]

Output

T4

SDRAM数据总线

sdram_dq[14]

Output

T3

SDRAM数据总线

sdram_dq[15]

Output

T2

SDRAM数据总线

ddc_scl

Output

P15

DDC时钟

dda_sda

Output

N14

DDC数据

tmds_clk_p

Output

R16

时钟差分信号

tmds_clk_n

Output

P16

时钟差分信号

tmds_data_p[2]

Output

K15

红色分量差分信号

tmds_data_n[2]

Output

K16

红色分量差分信号

tmds_data_p[1]

Output

L15

绿色分量差分信号

tmds_data_n[1]

Output

L15

绿色分量差分信号

tmds_data_p[0]

Output

N15

蓝色分量差分信号

tmds_data_n[0]

Output

N16

蓝色分量差分信号

下面进行管脚分配,管脚的分配方法在前面章节已有所讲解,在此就不再过多叙述,管脚的分配如下图 71‑3、图 71‑4所示。

Enetpi004

图 71‑3 管脚分配

Enetpi005

图 71‑4 管脚分配

21.4.1.1. 结果验证

如图 71‑5所示,开发板连接12V直流电源、USB-Blaster下载器JTAG端口、网线以及HDMI显示器,网线另一端连接路由器;PC机也使用另一条网线与路由器相连。线路正确连接后,打开开关为板卡上电。

Enetpi006

图 71‑5 程序下载连线图

以管理员身份运行“cmd.exe”输入命令“netsh i i show in”,查看要进行ARP绑定的网卡的idx编号,记住名称为“以太网”的idx编号,如图 71‑6所示。

Enetpi007

图 71‑6 查看idx编号

在“cmd.exe”输入命令“arp -a”,查看接口的ARP缓存表,找到与“以太网”的idx编号相同的接口,如图 71‑7所示。

Enetpi008

图 71‑7 以太网接口

将代码中“PC_IP”改为与此接口相同的IP地址;“PC_MAC”改为PC机的MAC地址,PC机MAC地址的查找方式,在“Quartus软件安装”章节有详细介绍;将板卡IP、MAC地址均改为全F,使用广播方式,确保板卡能够接收到以太网数据,如图 71‑8所示。

Enetpi009

图 71‑8 IP、MAC地址的设置

如图 71‑9所示,重新编译工程,使用“Programmer”为开发板下载程序。

Enetpi010

图 71‑9 程序下载连线图

程序下载完成后,稍等片刻,打开网络调试助手,如图 71‑10所示。

Enetpi011

图 71‑10 打开网络调试助手

如图 71‑11所示,配置网络调试助手的IP和端口号,启用文件数据源发送数据,发送的数据源为“doc”文件夹下的Bin文件。

Enetpi012

图 71‑11 启用文件数据源发送图片数据

HDMI显示器会显示出网络调试助手发送的图片数据,如图 71‑12、图 71‑13所示。

Enetpi013

图 71‑12 HDMI显示以太网传输图片(一)

Enetpi014

图 71‑13 HDMI显示以太网传输图片(二)

HDMI显示器能够正确显示图片,验证通过。读者也可使用与“以太网数据回环”章节相同的验证方法进行验证。

21.5. 章末总结

本章节中,我们将以太网、SDRAM、VGA、HDMI的相关知识结合起来,设计并实现了基于RMII接口UDP协议的以太网图片传输HDMI显示的实验工程,目的是为了加深读者对以太网相关知识的理解,同时也是为了巩固前面章节的SDRAM、VGA、HDMI等部分的相关知识,望读者务必掌握扎实,如有遗忘,可回顾 相关章节重新学习巩固。