20. 基于以太网传输的图片显示(VGA)¶
在“以太网数据环回实验”章节,我们设计并实现了以太网数据收发器,通过实验实现了PC端与板卡的数据交互。在本章节,我们使用已经通过验证的以太网数据收发器,实现基于以太网传输的VGA图片显示。
20.1. 理论学习¶
本章节涉及到的PLL时钟IP核、SDRAM、VGA以及以太网的相关知识在前面章节均有详细讲解,此处不再赘述。读者若有遗忘,请回顾相关章节进行学习巩固。
20.2. 实战演练¶
20.2.1. 实验目标¶
通过实验设计并实现基于以太网传输的VGA图片显示。在PC端通过网络调试助手向板卡发送图片数据源文件,FPGA通过以太网数据收发器接收并提取图片数据,将图片数据暂存到SDRAM中,VGA驱动模块读取暂存在SDRAM的图片数据并显示在VGA显示器上,分辨率为640*480。
20.2.2. 硬件资源¶
硬件资源部分,读者可参阅“SDRAM读写控制器的设计与验证”“VGA显示器驱动设计与验证”和“以太网数据回环实验”章节的硬件资源小节。
20.3. 程序设计¶
20.3.1. 整体说明¶
实验目标已经明确,硬件资源也已经介绍完毕,我们开始程序设计部分的讲解。我们先来对实验工程进行一个整体说明,让读者了解整个实验工程的框架结构。实验工程整体框图,具体见图 70‑1;子功能模块简介,具体见表格 70‑1。
图 70‑1 实验工程整体框图
表格 70‑1 子功能模块功能描述
模块名称 |
功能描述 |
---|---|
clk_gen |
时钟信号生成模块(IP核) |
data32_to_data16 |
数据位宽转换模块 |
eth_udp_mii |
以太网数据收发器 |
rmii_to_mii |
RMII接口转MII接口模块 |
vga_ctrl |
VGA显示驱动模块 |
sdram_top |
SDRAM数据读写控制器 |
eth_vga_pic |
顶层模块 |
由上述图表可知,实验工程包含6个子功能模块:时钟信号生成模块(clk_gen(IP核))、数据位宽转换模块(data32_to_data16)、RMII接口转MII接口模块(rmii_to_mii)、以太网数据收发器 (eth_udp_mii)、VGA显示驱动模块(vga_ctrl)、SDRAM数据读写控制器(sdram_top)和顶层模块(eth_vga_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中读取图片数据并显示;SDRAM数据读写控制器(sdram_top),暂存自以太网数据包中读出的图片数据;顶层模块(eth_vga_pic),例化子功能模块 ,连接各自对应信号。
这一部分我们对实验工程整体做了简要介绍,工程涉及的部分子功能模块在前面章节已有详细介绍,下面我们将会对实验工程涉及的未讲解的子功能模块进行解释和说明。
20.3.1.1. 以太网数据接收模块¶
本实验工程使用了前面章节设计的以太网数据收发器,为适应本实验工程,对以太网数据收发器中的以太网数据接收模块做了一些改动,下面我们针对改动部分做一下解释说明。
本实验工程要实现基于以太网传输的VGA图片显示,待显示图片经过预处理生成可被网络调试助手直接发送的BIN文件(图片具体处理方法参照“基于SD卡的VGA图片显示”章节相关内容)。
PC端使用网络调试助手将图片BIN文件发送给板卡,网络调试助手以以太网数据包的格式发送数据,每包包含有效图像数据8192字节,分辨率为640*480的图片被分为75个数据包发送(640 * 480 * 2 / 8192 = 75 )。
由于单包数据不能超过1.5k字节,PC端网卡将单包数据分为6个小包进行发送。我们使用WireShark对数据包进行抓取,如图 70‑2、图 70‑3所示。
图 70‑2 以太网数据包(一)
图 70‑3 以太网数据包(二)
由图可知,网络调试助手发出的大包数据被分为6个小包,75个大包数据本分为450个小包,6包一个循环。抽取来自一包的6小数据包,观察包内数据。
图 70‑4 以太网数据包(三)
由图 70‑4可知,6个数据包中,前5个数据包包含数据长度为1514字节,第6个数据包包含数据834个字节,6数据包共包含有效图像字节8192个。
图 70‑5 以太网数据包(四)
由图可知,第一包数据中有1514个字节,其中包括以太网帧头(14字节)、IP首部(20字节)和UDP首部(8字节),有效数据由1472字节。
图 70‑6 以太网数据包(五)
图 70‑7 以太网数据包(六)
图 70‑8 以太网数据包(七)
图 70‑9 以太网数据包(八)
由图可知,第二、三、四、五包数据中有1514个字节,其中包括以太网帧头(14字节)、IP首部(20字节),有效数据1480字节,无UDP首部。
图 70‑10 以太网数据包(九)
由图可知,第六包数据中有834个字节,其中包括以太网帧头(14字节)、IP首部(20字节),有效数据有800字节,无UDP首部。
综上所述,未修改的以太网数据接收模块不能从传入数据包中提取有效数据字节数这一信息,没有这一信息,模块不能实现有效数据提取。
修改后的模块代码中,对有效数据长度进行循环赋值。声明计数器对传入数据包进行循环计算,初值为0,计数范围0-5;计数为0时,有效数据长度赋值为1472;计数为5时,有效数据长度赋值为800;其他计数值,有效数据长度赋值为1480。相关部分代码如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //data_len:有效数据字节长度
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_len <= 16'd0;
else if(cnt_pkb == 3'd0)
data_len <= 16'd1472;
else if(cnt_pkb == 3'd5)
data_len <= 16'd800;
else
data_len <= 16'd1480;
//cnt_pkb:数据包计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_pkb <= 3'd0;
else if(rd_data_fall == 1'b1)
if(cnt_pkb == 3'd5)
cnt_pkb <= 3'd0;
else
cnt_pkb <= cnt_pkb + 1'b1;
else
cnt_pkb <= cnt_pkb;
|
修改后的模块参考代码,具体见代码清单 70‑1。
代码清单 70‑1 以太网数据接收模块参考代码(ip_receive.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 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 | module ip_receive
#(
parameter BOARD_MAC = 48'hFF_FF_FF_FF_FF_FF , //板卡MAC地址
parameter BOARD_IP = 32'hFF_FF_FF_FF //板卡IP地址
)
(
input wire sys_clk , //时钟信号
input wire sys_rst_n , //复位信号,低电平有效
input wire eth_rxdv , //数据有效信号
input wire [3:0] eth_rx_data , //输入数据
output reg rec_data_en , //数据接收使能信号
output reg [31:0] rec_data , //接收数据
output reg rec_end , //数据包接收完成信号
output reg [15:0] rec_data_num //接收数据字节数
);
////
//\* Parameter and Internal Signal \//
////
//parameter define
localparam IDLE = 7'b000_0001, //初始状态
PACKET_HEAD = 7'b000_0010, //接收数据包头
ETH_HEAD = 7'b000_0100, //接收以太网首部
IP_HEAD = 7'b000_1000, //接收IP首部
UDP_HEAD = 7'b001_0000, //接收UDP首部
REC_DATA = 7'b010_0000, //接收数据
REC_END = 7'b100_0000; //单包数据传输结束
//wire define
wire ip_flag ; //IP地址正确标志
wire mac_flag ; //MAC地址正确标志
wire rd_data_fall ; //数据接收状态下降沿
//reg define
reg eth_rxdv_reg ; //数据有效信号打拍
reg [3:0] eth_rx_data_reg ; //输入数据打拍
reg data_sw_en ; //数据拼接使能信号
reg data_en ; //拼接后的数据使能信号
reg [7:0] data ; //拼接后的数据
reg [6:0] state ; //状态机状态变量
reg sw_en ; //状态跳转标志信号
reg err_en ; //数据读取错误信号
reg [4:0] cnt_byte ; //字节计数器
reg [47:0] des_mac ; //目的MAC地址,本模块中表示开发板MAC地址
reg [31:0] des_ip ; //目的IP地址,本模块中表示开发板IP地址
reg [5:0] ip_len ; //IP首部字节长度
reg [15:0] udp_len ; //UDP部分字节长度
reg [15:0] data_len ; //有效数据字节长度
reg [15:0] cnt_data ; //接收数据字节计数器
reg [1:0] cnt_rec_data ; //数据计数器,单位4字节
reg [2:0] cnt_pkb ; //数据包计数器
reg rd_data_flag_d1 ; //数据接收状态标志信号打一拍
reg rd_data_flag_d2 ; //数据接收状态标志信号打两拍
////
//\* Main Code \//
////
//eth_rxdv_reg:数据有效信号打拍
always@(negedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
eth_rxdv_reg <= 1'b0;
else
eth_rxdv_reg <= eth_rxdv;
//eth_rx_data_reg:输入数据打拍
always@(negedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
eth_rx_data_reg <= 4'b0;
else
eth_rx_data_reg <= eth_rx_data;
//data_sw_en:数据拼接使能
always@(negedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_sw_en <= 1'b0;
else if(eth_rxdv_reg == 1'b1)
data_sw_en <= ~data_sw_en;
else
data_sw_en <= 1'b0;
//data_en:拼接后的数据使能信号
always@(negedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_en <= 1'b0;
else
data_en <= data_sw_en;
//data:拼接后的数据
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data <= 8'b0;
else if((eth_rxdv_reg == 1'b1) && (data_sw_en == 1'b0))
data <= {eth_rx_data,eth_rx_data_reg};
else
data <= data;
//state:状态机状态变量
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
state <= IDLE;
else case(state)
IDLE:
if(sw_en == 1'b1)
state <= PACKET_HEAD;
else
state <= IDLE;
PACKET_HEAD:
if(sw_en == 1'b1)
state <= ETH_HEAD;
else if(err_en == 1'b1)
state <= REC_END;
else
state <= PACKET_HEAD;
ETH_HEAD:
if(sw_en == 1'b1)
state <= IP_HEAD;
else if(err_en == 1'b1)
state <= REC_END;
else
state <= ETH_HEAD;
IP_HEAD:
if(sw_en == 1'b1)
if(cnt_pkb == 3'd0)
state <= UDP_HEAD;
else
state <= REC_DATA;
else if(err_en == 1'b1)
state <= REC_END;
else
state <= IP_HEAD;
UDP_HEAD:
if(sw_en == 1'b1)
state <= REC_DATA;
else
state <= UDP_HEAD;
REC_DATA:
if(sw_en == 1'b1)
state <= REC_END;
else
state <= REC_DATA;
REC_END:
if(sw_en == 1'b1)
state <= IDLE;
else
state <= REC_END;
default:state <= IDLE;
endcase
//sw_en:状态跳转标志信号
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
sw_en <= 1'b0;
else if((state == IDLE) && (data_en == 1'b1) && (data == 8'h55))
sw_en <= 1'b1;
else if((state == PACKET_HEAD) && (data_en == 1'b1)
&& (cnt_byte == 5'd6) && (data == 8'hd5))
sw_en <= 1'b1;
else if((state == ETH_HEAD) && (data_en == 1'b1) && (cnt_byte == 8'd13)
&& ((des_mac == BOARD_MAC) \|\| (des_mac == 48'hFF_FF_FF_FF_FF_FF)))
sw_en <= 1'b1;
else if((state == IP_HEAD) && (data_en == 1'b1)
&& (cnt_byte == ip_len - 1'b1) && (des_ip[23:0] == BOARD_IP[31:8])
&& (data == BOARD_IP[7:0]))
sw_en <= 1'b1;
else if((state == UDP_HEAD) && (data_en == 1'b1) && (cnt_byte == 8'd7))
sw_en <= 1'b1;
else if((state == REC_DATA) && (data_en == 1'b1)
&& (cnt_data == data_len - 1'b1))
sw_en <= 1'b1;
else if((state == REC_END) && (eth_rxdv_reg == 1'b0) && (sw_en == 1'b0))
sw_en <= 1'b1;
else
sw_en <= 1'b0;
//err_en:数据读取错误信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
err_en <= 1'b0;
else if((state == PACKET_HEAD) && (data_en == 1'b1)
&& (cnt_byte < 5'd6) && (data != 8'h55))
err_en <= 1'b1;
else if((state == PACKET_HEAD) && (data_en == 1'b1)
&& (cnt_byte == 5'd6) && (data != 8'hd5))
err_en <= 1'b1;
else if((state == ETH_HEAD) && (data_en == 1'b1)
&& (cnt_byte == 5'd13) && (mac_flag == 1'b0))
err_en <= 1'b1;
else if((state == IP_HEAD) && (data_en == 1'b1)
&& (cnt_byte == 5'd19) && (ip_flag == 1'b0))
err_en <= 1'b1;
else
err_en <= 1'b0;
//cnt_byte:字节计数器
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_byte <= 5'd0;
else if((state == PACKET_HEAD) && (data_en == 1'b1))
if(cnt_byte == 5'd6)
cnt_byte <= 5'd0;
else
cnt_byte <= cnt_byte + 5'd1;
else if((state == ETH_HEAD) && (data_en == 1'b1))
if(cnt_byte == 5'd13)
cnt_byte <= 5'd0;
else
cnt_byte <= cnt_byte + 5'b1;
else if((state == IP_HEAD) && (data_en == 1'b1))
if(cnt_byte == 5'd19)
if(ip_flag == 1'b1)
begin
if(cnt_byte == ip_len - 1'b1)
cnt_byte <= 5'd0;
end
else
cnt_byte <= 5'd0;
else if(cnt_byte == ip_len - 1'b1)
cnt_byte <= 5'd0;
else
cnt_byte <= cnt_byte + 5'd1;
else if((state == UDP_HEAD) && (data_en == 1'b1))
if(cnt_byte == 5'd7)
cnt_byte <= 5'd0;
else
cnt_byte <= cnt_byte + 5'd1;
else
cnt_byte <= cnt_byte;
//des_mac:目的MAC地址,本模块中表示开发板MAC地址
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
des_mac <= 48'h0;
else if((state == ETH_HEAD) && (data_en == 1'b1)
&& (cnt_byte < 8'd6))
des_mac <= {des_mac[39:0],data};
//mac_flag:MAC地址正确标志
assign mac_flag = ((state == ETH_HEAD) && (des_mac == BOARD_MAC))
? 1'b1 : 1'b0;
//des_ip:目的IP地址,本模块中表示开发板IP地址
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
des_ip <= 32'd0;
else if((state == IP_HEAD) && (data_en == 1'b1)
&& (cnt_byte > 5'd15) && (cnt_byte <= 5'd19))
des_ip <= {des_ip[23:0],data};
else
des_ip <= des_ip;
//ip_flag:IP地址正确标志
assign ip_flag = ((des_ip[23:0] == BOARD_IP[31:8])
&& (data == BOARD_IP[7:0])) ? 1'b1 : 1'b0;
//ip_len:IP首部字节长度
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
ip_len <= 6'b0;
else if((state == IP_HEAD) && (data_en == 1'b1)
&& (cnt_byte == 8'd0))
ip_len <= {data[3:0],2'b00};
//udp_len:UDP部分字节长度
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
udp_len <= 16'd0;
else if((state == UDP_HEAD) && (data_en == 1'b1)
&& (cnt_byte >= 8'd4) && (cnt_byte <= 8'd5))
udp_len <= {udp_len[7:0],data};
//data_len:有效数据字节长度
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data_len <= 16'd0;
else if(cnt_pkb == 3'd0)
data_len <= 16'd1472;
else if(cnt_pkb == 3'd5)
data_len <= 16'd800;
else
data_len <= 16'd1480;
//cnt_data:接收数据字节计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_data <= 16'd0;
else if((state == REC_DATA) && (data_en == 1'b1))
if(cnt_data == (data_len - 1'b1))
cnt_data <= 16'd0;
else
cnt_data <= cnt_data + 16'd1;
// rec_data_en:数据接收使能信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rec_data_en <= 1'b0;
else if((state == REC_DATA) && (data_en == 1'b1)
&& ((cnt_data == (data_len - 1'b1)) \|\| (cnt_rec_data == 2'd3)))
rec_data_en <= 1'b1;
else
rec_data_en <= 1'b0;
//rec_data:接收数据
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rec_data <= 32'b0;
else if((state == REC_DATA) && (data_en == 1'b1))
if(cnt_rec_data == 2'd0)
rec_data[31:24] <= data;
else if(cnt_rec_data == 2'd1)
rec_data[23:16] <= data;
else if(cnt_rec_data == 2'd2)
rec_data[15:8] <= data;
else if(cnt_rec_data==2'd3)
rec_data[7:0] <= data;
else
rec_data <= rec_data;
else
rec_data <= rec_data;
//rec_data_num:接收数据字节数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rec_data_num <= 16'b0;
else if((state == REC_DATA) && (data_en == 1'b1)
&& (cnt_data == (data_len - 1'b1)))
rec_data_num <= data_len;
//cnt_rec_data:数据计数器,单位4字节
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_rec_data <= 2'd0;
else if((state == REC_DATA) && (data_en == 1'b1))
if(cnt_data == (data_len - 1'b1))
cnt_rec_data <= 2'd0;
else
cnt_rec_data <= cnt_rec_data + 2'd1;
//rd_data_flag_d1,rd_data_flag_d2
//数据接收状态标志信号打一拍,数据接收状态标志信号打两拍
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
rd_data_flag_d1 <= 1'b0;
rd_data_flag_d2 <= 1'b0;
end
else
begin
rd_data_flag_d1 <= state[5];
rd_data_flag_d2 <= rd_data_flag_d1;
end
//rd_data_fall:数据接收状态下降沿
assign rd_data_fall = ((rd_data_flag_d1 == 1'b0)
&& (rd_data_flag_d2 == 1'b1))
? 1'b1 : 1'b0;
//cnt_pkb:数据包计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_pkb <= 3'd0;
else if(rd_data_fall == 1'b1)
if(cnt_pkb == 3'd5)
cnt_pkb <= 3'd0;
else
cnt_pkb <= cnt_pkb + 1'b1;
else
cnt_pkb <= cnt_pkb;
//rec_end:数据包接收完成信
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rec_end <= 1'b0;
else if((state == REC_DATA) && (data_en == 1'b1)
&& (cnt_data == (data_len - 1'b1)))
rec_end <= 1'b1;
else
rec_end <= 1'b0;
endmodule
|
20.3.1.2. 数据位宽转换模块¶
模块框图
自以太网数据包中提取的图片数据位宽为32位,想要将其暂存到位宽为16位的SDRAM控制器中,就需要转换位宽。数据位宽转换模块就是为这一目的设计并实现的。模块框图,具体见图 70‑11;输入输出信号简介,具体见表格 70‑2。
图 70‑11 数据位宽转换模块框图
表格 70‑2 输入输出端口简介
信号 |
位宽 |
类型 |
功能描述 |
---|---|---|---|
sys_clk |
1bit |
input |
模块工作时钟 |
sys_rst_n |
1bit |
input |
复位信号,低有效 |
rec_en_in |
1bit |
input |
输入数据使能信号 |
rec_data_in |
32bit |
input |
输入32位宽数据 |
rec_en_out |
1bit |
output |
输出数据使能信号 |
rec_data_out |
16bit |
output |
输出16位宽数据 |
由上述图表可知,本模块输入输出信号共6路,输入信号4路,输出信号2路。
输入信号中,时钟信号sys_clk,由外部PHY芯片传入(eth_rx_clk),频率25MHz,作为模块工作时钟;复位信号sys_rst_n,低电平有效,由板卡复位按键传入;输入数据rec_data_in,是拼接后的以太网有效数据,为宽32位;与输入数据同步传入的还有输入数据使能信号rec_en_ in。输出信号中,输出数据rec_data_out,是拆分后额额待存入数据,位宽16位;与其成对出现的是输出数据使能信号rec_data_en。
波形图绘制
在模块框图小节,我们已经对数据位宽转换模块的模块功能和输入输出端口做了说明。接下来我们通过波形图的绘制,为读者说明各信号的设计与实现方法,以及模块的功能实现。模块整体波形图,具体见图 70‑12。
图 70‑12 模块整体波形图
模块输入信号有4路,输入工作时钟sys_clk,由PHY芯片(eth_rx_clk)传入,频率25MHz;输入复位信号sys_rst_n,低电平有效;每8个时钟周期输入一次有效数据rec_data_in,位宽为32位,因为根据以太网数据接收时序,一个eth_rx_clk时钟周期,只能接收有效数据4b it,完成32bit的数据接收需要8个时钟周期;与输入有效数据同步输入的还有输入数据使能信号rec_en_in。
已知输入信号时序,如和实现数据位宽的转换呢?
已知输入有效数据每8个时钟周期完成一次数据切换,输入数据使能信号在数据切换的第一个时钟周期为高电平,其他时刻为低电平。要实现32位数据到16位数据的位宽转换,在数据保持的8个时钟周期要对输入数据的高16位和低16位分别提取。
首先,在有效数据输入的第2个时钟周期,对输入数据的高16位提取,赋值给输出数据rec_data_out,输入数据使能信号赋值给输出数据使能信号rec_en_out;然后,声明寄存器信号rec_en_in_dX对输入数据使能信号进行打拍,本模块打了4拍,将打拍后的使能信号rec_en_in_d4赋值给 输出数据使能信号rec_en_out,同时提取输入数据的低16位赋值给输出数据rec_data_out,这样就完成了32位数据向16位数据的转换。
代码编写
参照绘制波形图,编写模块参考代码。模块参考代码具体见代码清单 70‑2。
代码清单 70‑2 数据位宽转换模块参考代码(data32_to_data_16.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 | module data32_to_data16
(
input wire sys_clk , //系统时钟
input wire sys_rst_n , //复位信号,低有效
input wire rec_en_in , //输入32位数据使能信号
input wire [31:0] rec_data_in , //输入32位数据
output reg rec_en_out , //输出16位数据使能信号
output reg [15:0] rec_data_out //输出16位数据
);
////
//\* Parameter and Internal Signal \//
////
//reg define
reg rec_en_in_d1;
reg rec_en_in_d2;
reg rec_en_in_d3;
reg rec_en_in_d4; //输入32位数据使能信号打拍
////
//\* Main Code \//
////
//rec_en_in_d1,rec_en_in_d2,rec_en_in_d3,rec_en_in_d4
//输入32位数据使能信号打拍
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
rec_en_in_d1 <= 1'b0;
rec_en_in_d2 <= 1'b0;
rec_en_in_d3 <= 1'b0;
rec_en_in_d4 <= 1'b0;
end
else
begin
rec_en_in_d1 <= rec_en_in;
rec_en_in_d2 <= rec_en_in_d1;
rec_en_in_d3 <= rec_en_in_d2;
rec_en_in_d4 <= rec_en_in_d3;
end
//rec_en_out:输出16位数据使能信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rec_en_out <= 1'b0;
else if((rec_en_in == 1'b1) \|\| (rec_en_in_d4 == 1'b1))
rec_en_out <= 1'b1;
else
rec_en_out <= 1'b0;
//rec_data_out:输出16位数据
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rec_data_out <= 16'b0;
else if(rec_en_in == 1'b1)
rec_data_out <= rec_data_in[31:16];
else if(rec_en_in_d4 == 1'b1)
rec_data_out <= rec_data_in[15:0];
else
rec_data_out <= rec_data_out;
endmodule
|
仿真文件编写
模块参考代码编写完毕,编写仿真文件对模块参考代码进行仿真验证。仿真文件参考代码,具体见代码清单 70‑3。
代码清单 70‑3 数据位宽转换模块仿真参考代码(tb_data32_to_data16.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 | module tb_data32_to_data16();
////
//\* Parameter and Internal Signal \//
////
//wire define
wire rec_en_out ; //输出16位数据使能信号
wire [15:0] rec_data_out; //输出16位数据
//reg define
reg eth_rx_clk ; //系统时钟
reg sys_rst_n ; //复位信号,低有效
reg rec_en_in ; //32位数据使能信号
reg [31:0] rec_data_in ; //32位数据
reg [2:0] cnt_data ; //数据间隔计数器
////
//\* Main Code \//
////
//时钟、复位信号
initial
begin
eth_rx_clk = 1'b1 ;
sys_rst_n <= 1'b0 ;
#200
sys_rst_n <= 1'b1 ;
end
always #20 eth_rx_clk = ~eth_rx_clk;
//cnt_data:数据间隔计数器
always@(posedge eth_rx_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_data <= 3'd0;
else
cnt_data <= cnt_data + 3'd1;
//rec_data_in:32位数据
always@(posedge eth_rx_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rec_data_in <= 32'h12_34_56_78;
else if(cnt_data == 3'd7)
rec_data_in <= rec_data_in + 1'b1;
else
rec_data_in <= rec_data_in;
//rec_en_in:32位数据使能信号
always@(posedge eth_rx_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rec_en_in <= 1'b0;
else if(cnt_data == 3'd7)
rec_en_in <= 1'b1;
else
rec_en_in <= 1'b0;
////
//\* Instantiation \//
////
//------------ data32_to_data16_inst -------------
data32_to_data16 data32_to_data16_inst
(
.sys_clk (eth_rx_clk ), //系统时钟
.sys_rst_n (sys_rst_n ), //复位信号,低有效
.rec_en_in (rec_en_in ), //输入32位数据使能信号
.rec_data_in (rec_data_in ), //输入32位数据
.rec_en_out (rec_en_out ), //输出16位数据使能信号
.rec_data_out (rec_data_out ) //输出16位数据
);
endmodule
|
仿真波形分析
使用ModelSim进行波形仿真,模块仿真结果如下,绘制波形图与仿真波形图各信号波形变化一致,模块仿真验证通过。
图 70‑13 数据位宽转换模块仿真波形
20.3.1.3. 顶层模块¶
模块框图
顶层模块作用较为简单,内部实例化各子功能模块,连接子模块对应信号;外部输入输出使能信号和以太网数据。在此我们只对顶层模块的整体框图和参考代码做一下介绍,无需绘制波形图。模块框图和输入输出信号简介,具体见图 70‑14、表格 70‑3。
图 70‑14 顶层模块框图
表格 70‑3 输入输出信号功能描述
信号 |
位宽 |
类型 |
功能描述 |
---|---|---|---|
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芯片复位信号,低电平有效 |
hsync |
1Bit |
Output |
输出行同步信号 |
vsync |
1Bit |
Output |
输出场同步信号 |
vga_rgb |
16Bit |
Output |
输出像素点色彩信息 |
代码编写
顶层模块参考代码,具体见代码清单 70‑4。
代码清单 70‑4 顶层模块参考代码(eth_vga_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 | module eth_vga_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数据总线
//VGA
output wire hsync , //输出行同步信号
output wire vsync , //输出场同步信号
output wire [15:0] vga_rgb //输出像素点色彩信息
);
////
//\* 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 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 eth_rxdv ; //输入数据有效信号(mii)
wire [3:0] eth_rx_data ; //输入数据(mii)
//reg define
reg mii_clk ; //mii时钟
////
//\* Main Code \//
////
//rst_n:/系统复位信号
assign rst_n = sys_rst_n & locked;
//mii_clk:mii时钟
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时钟,相位偏移
.locked (locked ) //时钟信号有效标志
);
//------------ rmii_to_mii_inst -------------
rmii_to_mii rmii_to_mii_inst
(
.eth_rmii_clk(eth_rx_clk ), //rmii时钟
.eth_mii_clk (mii_clk ), //mii时钟
.sys_rst_n (sys_rst_n ), //复位信号
.rx_dv (eth_rxdv_r ), //输入数据有效信号(rmii)
.rx_data (eth_rx_data_r ), //输入数据(rmii)
.eth_rx_dv (eth_rxdv ), //输入数据有效信号(mii)
.eth_rx_data (eth_rx_data ) //输入数据(mii)
);
//------------ 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 ), //mii时钟,接收
.sys_rst_n (rst_n ), //复位信号,低电平有效
.eth_rxdv (eth_rxdv ), //输入数据有效信号(mii)
.eth_rx_data (eth_rx_data ), //输入数据(mii)
.eth_tx_clk ( ), //mii时钟,发送
.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 ), //输出数据有效信号(mii)
.eth_tx_data ( ), //输出数据(mii)
.eth_rst_n (eth_rst_n ) //复位信号,低电平有效
);
//------------ 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 ), //输入待显示像素信息
.pix_data_req(rd_en ), //VGA数据请求信号
.hsync (hsync ), //输出行同步信号
.vsync (vsync ), //输出场同步信号
.rgb (vga_rgb ) //输出像素信息
);
endmodule
|
20.4. 上板调试¶
20.4.1. 引脚约束¶
仿真验证通过后,准备上板验证,上板验证之前先要进行引脚约束。工程中各输入输出信号与开发板引脚对应关系如表格 55‑3所示。
表格 70‑4 引脚分配表
信号名 |
信号类型 |
对应引脚 |
备注 |
---|---|---|---|
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数据总线 |
hsync |
Output |
C2 |
行同步信号 |
vsync |
Output |
D1 |
场同步信号 |
vga_rgb[15] |
Output |
A5 |
RGB色彩信息(红) |
vga_rgb[14] |
Output |
E6 |
RGB色彩信息(红) |
vga_rgb[13] |
Output |
E7 |
RGB色彩信息(红) |
vga_rgb[12] |
Output |
B8 |
RGB色彩信息(红) |
vga_rgb[11] |
Output |
A8 |
RGB色彩信息(红) |
vga_rgb[10] |
Output |
F8 |
RGB色彩信息(绿) |
vga_rgb[9] |
Output |
E8 |
RGB色彩信息(绿) |
vga_rgb[8] |
Output |
B7 |
RGB色彩信息(绿) |
vga_rgb[7] |
Output |
A7 |
RGB色彩信息(绿) |
vga_rgb[6] |
Output |
F7 |
RGB色彩信息(绿) |
vga_rgb[5] |
Output |
F6 |
RGB色彩信息(绿) |
vga_rgb[4] |
Output |
B6 |
RGB色彩信息(蓝) |
vga_rgb[3] |
Output |
A6 |
RGB色彩信息(蓝) |
vga_rgb[2] |
Output |
B5 |
RGB色彩信息(蓝) |
vga_rgb[1] |
Output |
A2 |
RGB色彩信息(蓝) |
vga_rgb[0] |
Output |
B4 |
RGB色彩信息(蓝) |
下面进行管脚分配,管脚的分配方法在前面章节已有所讲解,在此就不再过多叙述,管脚的分配如下图 70‑15、图 70‑16所示。
图 70‑15 管脚分配
图 70‑16 管脚分配
20.4.1.1. 结果验证¶
如图 70‑17所示,开发板连接12V直流电源、USB-Blaster下载器JTAG端口、网线以及VGA显示器,网线另一端连接路由器;PC机也使用另一条网线与路由器相连。线路正确连接后,打开开关为板卡上电。
图 70‑17 程序下载连线图
以管理员身份运行“cmd.exe”输入命令“netsh i i show in”,查看要进行ARP绑定的网卡的idx编号,记住名称为“以太网”的idx编号,如图 70‑18所示。
图 70‑18 查看idx编号
在“cmd.exe”输入命令“arp -a”,查看接口的ARP缓存表,找到与“以太网”的idx编号相同的接口,如图 70‑19所示。
图 70‑19 以太网接口
将代码中“PC_IP”改为与此接口相同的IP地址;“PC_MAC”改为PC机的MAC地址,PC机MAC地址的查找方式,在“Quartus软件安装”章节有详细介绍;将板卡IP、MAC地址均改为全F,使用广播方式,确保板卡能够接收到以太网数据,如图 70‑20所示。
图 70‑20 IP、MAC地址的设置
如图 70‑21所示,重新编译工程,使用“Programmer”为开发板下载程序。
图 70‑21 程序下载图
程序下载完成后,稍等片刻,打开网络调试助手,如图 70‑22所示。
图 70‑22 打开网络调试助手
如图 70‑23所示,配置网络调试助手的IP和端口号,启用文件数据源发送数据,发送的数据源为“doc”文件夹下的Bin文件。
图 70‑23 启用文件数据源发送图片数据
VGA显示器会显示出网络调试助手发送的图片数据,如图 70‑24、图 70‑25所示。
图 70‑24 VGA显示以太网传输图片(一)
图 70‑25 VGA显示以太网传输图片(二)
VGA显示器能够正确显示图片,验证通过。读者也可使用与“以太网数据回环”章节相同的验证方法进行验证。
20.5. 章末总结¶
本章节中,我们将以太网、SDRAM、VGA的相关知识结合起来,设计并实现了基于RMII接口UDP协议的以太网图片传输VGA显示的实验工程,目的是为了加深读者对以太网相关知识的理解,同时也是为了巩固前面章节的SDRAM、VGA等部分的相关知识,望读者务必掌握扎实,如有遗忘,可回顾相关章节重新学习巩固。