1)实验平台:正点原子开拓者
FPGA 开发板
2)摘自《开拓者FPGA开发指南》关注官方微信号公众号,获取更多资料:正点原子
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-13912-1-1.html
第四十四章 以太网传输图片(VGA显示)
我们在“基于ROM的VGA图片显示实验”中利用FPGA片上存储资源存储图片,并通过VGA将
图片显示到显示器屏幕上。但是由于FPGA片上存储资源有限,只能存储分辨率较小的图片。在
本章,我们将学习如何利用SDRAM来存储图片,并通过VGA接口显示。
本章包括以下几个部分:
44.1 SDRAM-VGA图片显示简介
44.2 实验任务
44.3 硬件设计
44.4 程序设计
44.5 下载验证
SDRAM-VGA图片显示简介
利用VGA接口显示图片时,需要一个存储器用于存储图片数据。这个存储器可以采用FPGA
片上存储资源,也可以使用片外存储设备,如SDRAM、SD卡、FLASH等。
由于FPGA的片上存储资源有限,所以能够存储的图片大小也受到限制。开拓者开发板上的
FPGA芯片型号为EP4CE10F17C8,它的片上存储资源为414Kbit,也就是说存储的图片大小不能
超过414Kbit。对于分辨率为640*480的图片,当采用RGB565数据格式时,所需要的存储空间为
640*480*16bit=4915200bit=4800Kbit(1Kbit=1024bit)。也就是说,即使是采用VGA最小的
显示分辨率——640*480,FPGA的片上存储资源也远远不能够满足图片存储的需求。
开拓者开发板上的SDRAM存储容量为256Mbit(1Mbit=1024Kbit),可以存储54张上述格式
的图片。另外,相比于开发板上的SD卡、FLASH等片外存储设备,SDRAM还具有读写速度快的优
点。因此,在利用VGA显示图片时,SDRAM是一种非常理想的存储设备。
然而,SDRAM不像SD卡一样可以事先将图片导入。作为易失性存储器,SDRAM中的数据在掉
电后会丢失,因此用于VGA图片显示时需要向SDRAM中写入图片数据。这里我们采用开发板上的
网口接收上位机发送的图片数据,然后将其写入SDRAM,最终通过VGA接口显示。
我们在“SDRAM读写测试实验”中对SDRAM作了详细的介绍,包括SDRAM存储结构、寻址方
法、操作时序等。如果大家对这部分内容不是很熟悉的话,请参考“SDRAM读写测试实验”中
的SDRAM简介部分。
实验任务
本章的实验任务是使用开拓者开发板上的网口接收上位机传输的图片(分辨率为640*480),
然后将图片存储在SDRAM中并通过VGA接口在显示器屏幕上显示。
硬件设计
SDRAM部分的硬件设计原理与“SDRAM读写测试实验”完全相同,请参考“SDRAM读写测试
实验”中的硬件设计部分。以太网接口部分的硬件设计请参考“以太网
通信实验”中的硬件设
计部分。VGA接口部分的硬件设计请参考“VGA彩条显示实验”中的硬件设计部分。
由于SDRAM、以太网接口和VGA接口的引脚数目较多且在前面相应的章节中已经给出它们的
管脚列表,这里不再列出管脚分配。
程序设计
图 44.4.1是根据本章实验任务画出的系统框图。上位机通过网线将图片以bin文件格式
传输到开发板上,UDP模块负责接收图片数据;然后将UDP模块输出的位宽为32bit的数据转成
16bit,并通过SDRAM控制器存入SDRAM;最后VGA驱动模块通过SDRAM控制器读取SDRAM中存储的
图片数据并通过VGA接口显示在显示器上。
图 44.4.1 以太网传输图片(VGA显示)实验系统框图
程序中各模块端口及信号连接如图 44.4.2所示:
图 44.4.2 顶层模块原理图
FPGA顶层(eth_sdram_vga)例化了以下五个模块:PLL时钟模块(pll_clk)、UDP模块(udp)、
32bit转16bit模块(udp_32_to_16bit)、SDRAM控制器模块(sdram_top)以及VGA驱动模块
(vga_driver)。
PLL时钟模块(pll_clk):本实验中VGA驱动模块所需要的像素时钟为25MHz,SDRAM控制
器工作在100MHz时钟频率下,另外还需要一个输出给SDRAM芯片的100MHz相位偏移时钟。因此
需要一个PLL模块用于产生系统各个模块所需的时钟频率。
UDP模块(udp):UDP模块实现以太网通信的收发功能,该模块内部例化了以太网接收模
块(ip_receive)、以太网发送模块(ip_send)和CRC32校验模块(crc32_d4)。由于本章实
验中网口只负责接收数据,所以其中以太网的发送功能并没有用到。有关该模块的详细介绍请
大家参考“以太网通信实验”章节。
32bit转16bit模块(udp_32_to_16bit):该模块将UDP模块输出的位宽为32bit的数据转
换成16bit,这是因为数据需要写入SDRAM,而SDRAM控制器的数据接口位宽为16bit。
SDRAM控制器模块(sdram_top):SDRAM控制器模块负责驱动SDRAM片外存储器。该模块将
SDRAM复杂的读写操作封装成类似FIFO的用户接口,非常方便用户的使用。有关该模块的详细
介绍请大家参考“SDRAM读写测试实验”章节。
VGA驱动模块(vga_driver):VGA驱动模块根据VGA时序参数输出行、场同步信号;同时
它还要输出数据请求信号用于读取SDRAM中的图片数据,并将图片通过VGA接口显示。
顶层模块的代码如下:
1
module eth_sdram_vga
(
2
input clk
, //FPGA外部时钟,50MHz
3
input rst_n
, //按键复位,低电平有效
4 //以太网接口
5
input eth_rx_clk
, //MII接收数据时钟
6
input eth_rxdv
, //MII输入数据有效信号
7
input [ 3
:0
] eth_rx_data
, //MII输入数据
8
output eth_tx_en
, //MII输出数据有效信号
9
output eth_rst_n
, //以太网芯片复位信号,低电平有效
10 //SDRAM接口
11
output sdram_clk
, //SDRAM 芯片时钟
12
output sdram_cke
, //SDRAM 时钟有效
13
output sdram_cs_n
, //SDRAM 片选
14
output sdram_ras_n
, //SDRAM 行有效
15
output sdram_cas_n
, //SDRAM 列有效
16
output sdram_we_n
, //SDRAM 写有效
17
output [ 1
:0
] sdram_ba
, //SDRAM Bank地址
18
output [12
:0
] sdram_addr
, //SDRAM 行/列地址
19
inout [15
:0
] sdram_data
, //SDRAM 数据
20
output [ 1
:0
] sdram_dqm
, //SDRAM 数据掩码
21 //VGA接口
22
output vga_hs
, //行同步信号
23
output vga_vs
, //场同步信号
24
output [15
:0
] vga_rgb //红绿蓝三原色输出
25
);
26
27 //parameter define
28 //开发板MAC地址 00-11-22-33-44-55
29
parameter BOARD_MAC
= 48'h00_11_22_33_44_55
;
30 //开发板IP地址 192.168.1.123
31
parameter BOARD_IP
= {8'd192
,8'd168
,8'd1
,8'd123
};
32 //目的MAC地址 ff_ff_ff_ff_ff_ff
33
parameter DES_MAC
= 48'hff_ff_ff_ff_ff_ff
;
34 //目的IP地址 192.168.1.102
35
parameter DES_IP
= {8'd192
,8'd168
,8'd1
,8'd102
};
36
37 //wire define
38
wire vga_clk
; //VGA驱动时钟,25MHz
39
wire clk_100m
; //SDRAM 控制器时钟
40
wire clk_100m_shift
; //相位偏移时钟
41
42
wire locked
; //PLL输出有效标志
43
wire sys_rst_n
; //系统复位信号
44
wire sdram_init_done
; //SDRAM 初始化完成标志
45
46
wire udp_rec_en
; //以太网接收的数据有效信号
47
wire [31
:0
] udp_rec_data
; //以太网接收的数据
48
49
wire wr_en
; //SDRAM 写端口:写使能
50
wire [15
:0
] wr_data
; //SDRAM 写端口:写入的数据
51
wire rd_en
; //SDRAM 读端口:读使能
52
wire [15
:0
] rd_data
; //SDRAM 读端口:读出的数据
53
54 //*****************************************************
55 //** main code
56 //*****************************************************
57
58 //待PLL输出稳定之后,停止系统复位
59
assign sys_rst_n
= rst_n
& locked
;
60
61 //例化PLL, 产生各模块所需要的时钟
62 pll_clk u_pll_clk
(
63
.inclk0
(clk
),
64
.areset
(~rst_n
),
65
66
.c0
(vga_clk
),
67
.c1
(clk_100m
),
68
.c2
(clk_100m_shift
),
69
.locked
(locked
)
70
);
71
72 //UDP模块接收网口数据
73 udp
#(
74
.BOARD_MAC
(BOARD_MAC
), //开发板MAC地址
75
.BOARD_IP
(BOARD_IP
), //开发板IP地址
76
.DES_MAC
(DES_MAC
), //目的MAC地址
77
.DES_IP
(DES_IP
) //目的IP地址
78
)
79 u_udp
(
80
.rst_n
(sys_rst_n
),
81
82
.eth_rx_clk
(eth_rx_clk
), //MII接收数据时钟
83
.eth_rxdv
(eth_rxdv
), //MII输入数据有效信号
84
.eth_rx_data
(eth_rx_data
), //MII输入数据
85
.eth_rst_n
(eth_rst_n
), //以太网芯片复位信号,低电平有效
86
.rec_en
(udp_rec_en
), //以太网接收的数据有效信号
87
.rec_data
(udp_rec_data
), //以太网接收的数据
88
.rec_pkt_done
(), //以太网单包数据接收完成信号
89
.rec_byte_num
(), //以太网接收的有效字节数 单位:byte
90
91
.eth_tx_clk
(),
92
.eth_tx_en
(eth_tx_en
), //MII输出数据有效信号
93
.tx_start_en
(),
94
.tx_data
(),
95
.tx_byte_num
(),
96
.tx_done
(),
97
.tx_req
(),
98
.eth_tx_data
()
99
);
100
101 //将UDP模块接收到的32bit数据转换成16bit数据
102 udp_32_to_16bit
(
103
.eth_rx_clk
(eth_rx_clk
),
104
.rst_n
(sys_rst_n
),
105
106
.udp_rec_en
(udp_rec_en
),
107
.udp_rec_data
(udp_rec_data
),
108
.udp_rec_en_16
(wr_en
),
109
.udp_rec_data_16
(wr_data
)
110
);
111
112 //SDRAM 控制器顶层模块,封装成FIFO接口
113 //SDRAM 控制器地址组成: {bank_addr[1:0],row_addr[12:0],col_addr[8:0]}
114 sdram_top u_sdram_top
(
115
.ref_clk
(clk_100m
), //sdram 控制器参考时钟
116
.out_clk
(clk_100m_shift
), //用于输出的相位偏移时钟
117
.rst_n
(sys_rst_n
), //系统复位
118
119 //用户写端口
120
.wr_clk
(eth_rx_clk
), //写端口FIFO: 写时钟
121
.wr_en
(wr_en
), //写端口FIFO: 写使能
122
.wr_data
(wr_data
), //写端口FIFO: 写数据
123
.wr_min_addr
(24'd0
), //写SDRAM的起始地址
124
.wr_max_addr
(24'd640
*24'd480
), //写SDRAM的结束地址
125
.wr_len
(10'd512
), //写SDRAM时的数据突发长度
126
.wr_load
(~sys_rst_n
), //写端口复位: 复位写地址,清空写FIFO
127
128 //用户读端口
129
.rd_clk
(vga_clk
), //读端口FIFO: 读时钟
130
.rd_en
(rd_en
), //读端口FIFO: 读使能
131
.rd_data
(rd_data
), //读端口FIFO: 读数据
132
.rd_min_addr
(24'd0
), //读SDRAM的起始地址
133
.rd_max_addr
(24'd640
*24'd480
), //读SDRAM的结束地址
134
.rd_len
(10'd512
), //从SDRAM中读数据时的突发长度
135
.rd_load
(~sys_rst_n
), //读端口复位: 复位读地址,清空读FIFO
136
137 //用户控制端口
138
.sdram_read_valid
(1'b1
), //SDRAM 读使能
139
.sdram_init_done
(sdram_init_done
), //SDRAM 初始化完成标志
140
141 //SDRAM 芯片接口
142
.sdram_clk
(sdram_clk
), //SDRAM 芯片时钟
143
.sdram_cke
(sdram_cke
), //SDRAM 时钟有效
144
.sdram_cs_n
(sdram_cs_n
), //SDRAM 片选
145
.sdram_ras_n
(sdram_ras_n
), //SDRAM 行有效
146
.sdram_cas_n
(sdram_cas_n
), //SDRAM 列有效
147
.sdram_we_n
(sdram_we_n
), //SDRAM 写有效
148
.sdram_ba
(sdram_ba
), //SDRAM Bank地址
149
.sdram_addr
(sdram_addr
), //SDRAM 行/列地址
150
.sdram_data
(sdram_data
), //SDRAM 数据
151
.sdram_dqm
(sdram_dqm
) //SDRAM 数据掩码
152
);
153
154 //VGA驱动模块
155 vga_driver u_vga_driver
(
156
.vga_clk
(vga_clk
),
157
.sys_rst_n
(sdram_init_done
),
158
159
.vga_hs
(vga_hs
),
160
.vga_vs
(vga_vs
),
161
.vga_rgb
(vga_rgb
),
162
163
.data_req
(rd_en
),
164
.pixel_data
(rd_data
),
165
.pixel_xpos
(),
166
.pixel_ypos
()
167
);
168
169
endmodule
顶层模块主要完成了对其他各个模块的例化。在代码的第27至35行定义了四个参量:开发
板MAC地址BOARD_MAC,开发板IP地址BOARD_IP,目的MAC地址DES_MAC(这里指上位机MAC地址),
目的IP地址DES_IP(PC IP地址)。开发板的MAC地址和IP地址是我们随意指定的,只要不和目
的MAC地址和目的IP地址一样就可以,否则会产生地址冲突。目的MAC地址这里写的是公共MAC
地址(48'hff_ff_ff_ff_ff_ff),也可以修改成电脑网口的MAC地址;DES_IP是对应上位机以
太网的IP地址。这里定义的四个参数是向下传递的,需要修改MAC地址或者IP地址时直接在这
里修改即可,而不用在udp模块里面修改。
由于VGA显示分辨率为640*480,所以SDRAM的读写结束地址均设置为“640*480”,如代码
中第124行和第133行所示。
32bit转16bit模块的代码如下:
1
module udp_32_to_16bit
(
2
input eth_rx_clk
, // MII接收数据时钟
3
input rst_n
,
4
5
input udp_rec_en
, //UDP模块接收数据有效信号
6
input [31
:0
] udp_rec_data
, //UDP模块接收的32位有效数据
7
8
output reg udp_rec_en_16
, //UDP模块接收16位数据有效信号
9
output reg [15
:0
] udp_rec_data_16 //UDP模块接收的16位有效数据
10
);
11
12 //reg define
13
reg rec_en_flag
;
14
15 //*****************************************************
16 //** main code
17 //*****************************************************
18
19 //将UDP模块的接收数据有效信号延时一个时钟周期
20
always @(posedge eth_rx_clk
or negedge rst_n
)begin
21
if(!rst_n
)
22 rec_en_flag
<=0
;
23
else
24 rec_en_flag
<= udp_rec_en
;
25
end
26
27 //分两个时钟周期输出UPP模块接收数据的高十六位和低十六位
28
always @(posedge eth_rx_clk
or negedge rst_n
)begin
29
if(!rst_n
) begin
30 udp_rec_en_16
<= 1'b0
;
31 udp_rec_data_16
<= 1'b0
;
32
end
33
else if(udp_rec_en
)begin
34 udp_rec_en_16
<= 1'b1
;
35 udp_rec_data_16
<= udp_rec_data
[31
:16
];
36
end
37
else if(rec_en_flag
)begin
38 udp_rec_en_16
<= 1'b1
;
39 udp_rec_data_16
<= udp_rec_data
[15
:0
];
40
end
41
else begin
42 udp_rec_en_16
<= 1'b0
;
43 udp_rec_data_16
<= udp_rec_data_16
;
44
end
45
end
46
47
endmodule
由于UDP模块将接收到网口数据转换成32bit数据输出,然而SDRAM控制器的数据接口位宽
为16bit。为了将数据写入SDRAM,需要将UDP模块输出的位宽为32bit的数据转成16bit。如程
序第27至45行所示,udp_32_to_16bit模块在每次检测到UDP数据有效信号(udp_rec_en)拉高
时,将32位有效数据分两个时钟周期输出,先输出高十六位,后输出低十六位。
需要注意的是,由于UDP接收的数据在写入SDRAM时,需要用UDP数据有效信号作为写使能,
因此在输出16bit有效数据(udp_rec_data_16)时,输出的数据有效信号(udp_rec_en_16)
需要拉高两个时钟周期。这就是程序第19至25行将UDP模块的接收数据有效信号延时一个时钟
周期的原因。
图 44.4.3为网口接收上位机发送数据时SignalTap抓取的波形图。从图中可以看到,在
UDP数据有效信号udp_rec_en拉高时,32位有效数据udp_rec_data的高16位和低16位在两个时
钟周期先后输出,同时16位数据有效信号udp_rec_en_16拉高两个时钟周期。
图 44.4.3 udp_32_to_16bit模块SignalTap波形图
下载验证
首先在eth_sdram_vga/par文件夹中双击“eth_sdram_vga.qpf”打开基于SDRAM的VGA图片
显示实验工程,工程打开后如图 44.5.1所示:
图 44.5.1 基于SDRAM的VGA图片显示工程
将网线一端连接电脑网口,另一端与开发板上的网口连接;然后将VGA连接线一端连接显
示器,另一端与开发板上的VGA接口连接;接下来将下载器一端连电脑,另一端与开发板上对
应端口连接,最后连接
电源线并打开电源开关。
开拓者开发板实物图如下所示:
图 44.5.2 开拓者开发板VGA接口和网口
接下来我们下载程序,验证基于SDRAM的VGA图片显示实验。
工程打开后通过点击工具栏中的“Programmer”图标打开下载界面,通过“Add File”按
钮选择eth_sdram_vga/par/output_files目录下的“eth_sdram_vga.sof”文件。开发板电源
打开后,在程序下载界面点击“Hardware Setup”,在弹出的对话框中选择当前的硬件连接为
“USB-Blaster[USB-0]”。然后点击“Start”将工程编译完成后得到的sof文件下载到开发板
中,如图 44.5.3所示:
图 44.5.3 程序下载界面
程序下载完成后,由于还没有向SDRAM中写入图片数据,所以SDRAM中的数据是随机的,此
时VGA显示器上显示的像素点颜色是杂乱无章的。接下来我们在上位机中将图片通过网口下载
到开发板上的SDRAM中。
图片最终是以bin文件的格式利用网口调试助手下载到开发板上的。我们先来介绍一下如
何利用工具“Img2Lcd”将图片转成bin文件,该工具位于开发板所随附的资料“6_软件资料/1_
软件/Img2Lcd”目录下找到“Img2Lcd.exe”并双击打开,软件打开后界面如图 44.5.4所示。
在工具界面左侧设置输出数据类型为“二进制(*.bin)”,输出灰度为“16位真彩色”,
最大宽度和高度分别为“640”和“480”,另外还需要勾选“高位在前(MSB First)”。设
置完成后在菜单栏中点击“打开”,然后在弹出的界面中选择一幅分辨率为640*480的jpg格式
图片。图片加载进来之后,在菜单栏中点击“保存”,并在弹出的界面中选择bin文件的保存
路径并输入文件名。
图 44.5.4 Img2Lcd工具界面
到这里我们已经成功地将图片转成了bin文件,接下来需要借助网口调试助手将bin文件下
载到开发板中。但是需要注意的是,由于发送的文件较大,需要先对网络适配器属性进行配置。
图 44.5.5 设备管理器
在Windows系统中的“计算机”图标上右击,选择“管理”,然后在弹出的“计算机管理”
界面中选择“设备管理器”。然后在网络适配器中找到网卡并右键选择属性,如图 44.5.5所
示。
在属性界面中选择“高级”,在属性栏中找到“巨型帧”,然后在右侧值中选择“9KB MTU”,
最后点击确定,如图 44.5.6所示。默认情况下,以太网的MTU(Maximum Transmission Unit,
最大传输单元)是1500字节。而巨型帧的设置把以太网帧格式的数据段扩展到了9KB,从而在
传输大文件时减少了数据帧的个数,减轻了网络设备处理帧头的额外开销,从而提高网络传输
性能。
图 44.5.6 巨型帧设置
除了配置网络适配器属性之外,上位机还需要绑定开发板的MAC地址和IP地址才能和开发
板进行网络通信。有关MAC地址和IP地址的绑定以及网络调试助手的使用方法请大家参考“以
太网通信实验”的下载验证部分。
网络调试助手的设置如图 44.5.7所示,在远程主机一栏选择开发板的IP地址和端口号
“192.168.1.123 :1234”;然后在发送区设置一栏勾选“启用文件数据源”,在弹出的界面
中选择刚刚生成的bin文件;最后点击右下角的“发送”按钮,即可将由图片生成的bin文件通
过网线发送到开拓者开发板。
图 44.5.7 网络调试助手发送bin文件
bin文件传输完成后,网络调试助手最下方会指示发送的数据量,单位是字节。一帧分辨
率为640*480的图片数据量为640*480*2=614400字节,之所以乘以2是因为RGB565格式的图片数
据每个像素点的大小为16bit(2字节)。因此图 44.5.7中下方的“TX:614400”说明一帧图片
发送完成,此时显示器上显示发送的图片,说明基于SDRAM的VGA显示实验下载验证成功,如图
44.5.8所示:
图 44.5.8 VGA显示器显示的图片