基于FPGA的以太网控制器设计(附代码)
大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。
今天给大侠带来基于FPGA的以太网控制器(MAC)设计,由于篇幅较长,分三篇。今天带来第三篇,下篇,程序的仿真与测试和总结。话不多说,上货。 这里也给出前两篇的超链接:
导读
当前,互联网已经极大地改变了我们的生产和生活。与之相适应的,在嵌入式系统的研究开发方面,也越来越重视网络功能。嵌入式系统已经不再局限于一个个孤立的控制、处理单元,而是走向网络集成化,从而实现了多个系统的集中控制、信息共享。 以太网(Ethernet)技术在嵌入式系统上的开发应用,已经成为当前嵌入式研究领域的技术热点之一。一方面,与传统的 RS-485、CAN 等相比较,以太网更加高速、通用,而且还可以直接与 Internet 相连接,提供更大范围的远程访问;此外,经过适当剪裁和优化的 TCP/IP 协议栈,也完全可以适应工业用途的需求。另一方面,相对于新兴的 USB 2.0、IEEE 1394 等总线,以太网技术在传输距离、布线成本以及控制软件的通用性上都有明显的优势。 基于以太网的嵌入式系统,在以下方面都有良好的应用前景:
• 工业:工业控制、网络仪表、远程的分布式数据采集……
• 家庭自动化:智能家庭、信息家电、家庭网关……
• 商业:远程销售平台、智能自动售货机、公共电话卡发行系统……
• 环保:水源和空气污染监测,防洪体系及水土质量监测、堤坝安全……
• 其他:交通管理、车辆导航、自动抄表……
因此在使用 FPGA 设计各种嵌入式应用系统时,需要考虑为系统提供以太网接口。本章将 通过 FPGA 实现一个以太网控制器(MAC)的实例,详细介绍实现过程。
第三篇内容摘要:本篇会介绍程序的仿真与测试和总结,包括顶层程序、外部 PHY 芯片模拟程序、仿真结果等相关内容。
四、程序的仿真与测试
上面已经介绍了程序的主要部分。为了检验程序是否实现预先设定的功能,需要编写仿真程序。 以太网控制器的仿真程序(Testbench)需要同时模拟数据通信的两端:主机(上层协议)和外部 PHY 芯片。因此,设计仿真程序的结构如图 12 所示。
图 12 以太网控制器程序 Testbench 的结构
从图 12 上可以看到仿真程序应该包括:顶层程序、模拟 PHY 程序、模拟主机程序和以太网控制程序。
4.1 顶层程序 顶层程序负责连接仿真程序的各个部分:模拟 PHY 程序、模拟主机程序和以太网控制程序。同时顶层程序需要控制仿真的进行,主要代码如下: - `include "eth_phy_defines.v"
- `include "wb_model_defines.v"
- `include "tb_eth_defines.v"
- `include "eth_defines.v"
- `include "timescale.v"
- module tb_ethernet();
- //寄存器与连线
- reg wb_clk;
- ……
- //连接以太网控制器
- eth_top eth_top(
- .wb_clk_i(wb_clk), .wb_rst_i(wb_rst),
- .wb_adr_i(eth_sl_wb_adr_i[11:2]), .wb_sel_i(eth_sl_wb_sel_i), .wb_we_i(eth_sl_wb_we_i),
- .wb_cyc_i(eth_sl_wb_cyc_i), .wb_stb_i(eth_sl_wb_stb_i), .wb_ack_o(eth_sl_wb_ack_o),
- .wb_err_o(eth_sl_wb_err_o), .wb_dat_i(eth_sl_wb_dat_i), .wb_dat_o(eth_sl_wb_dat_o),
- .m_wb_adr_o(eth_ma_wb_adr_o), .m_wb_sel_o(eth_ma_wb_sel_o), .m_wb_we_o(eth_ma_wb_we_o), .m_wb_dat_i(eth_ma_wb_dat_i), .m_wb_dat_o(eth_ma_wb_dat_o), .m_wb_cyc_o(eth_ma_wb_cyc_o),
- .m_wb_stb_o(eth_ma_wb_stb_o), .m_wb_ack_i(eth_ma_wb_ack_i), .m_wb_err_i(eth_ma_wb_err_i),
- //发送数据
- .mtx_clk_pad_i(mtx_clk), .mtxd_pad_o(MTxD), .mtxen_pad_o(MTxEn), .mtxerr_pad_o(MTxErr),
- //接收数据部分
- .mrx_clk_pad_i(mrx_clk), .mrxd_pad_i(MRxD), .mrxdv_pad_i(MRxDV), .mrxerr_pad_i(MRxErr),
- .mcoll_pad_i(MColl), .mcrs_pad_i(MCrs),
- //媒体无关接口模块
- .mdc_pad_o(Mdc_O), .md_pad_i(Mdi_I), .md_pad_o(Mdo_O), .md_padoe_o(Mdo_OE),
- .int_o(wb_int)
- )
- //连接模拟 PHY 部分
- assign Mdio_IO = Mdo_OE ? Mdo_O : 1'bz ;
- assign Mdi_I = Mdio_IO;
- integer phy_log_file_desc;
- eth_phy eth_phy(
- .m_rst_n_i(!wb_rst),
- // MAC 发送数据
- .mtx_clk_o(mtx_clk), .mtxd_i(MTxD), .mtxen_i(MTxEn), .mtxerr_i(MTxErr),
- // MAC 接收数据
- .mrx_clk_o(mrx_clk), .mrxd_o(MRxD), .mrxdv_o(MRxDV), .mrxerr_o(MRxErr),
- .mcoll_o(MColl), .mcrs_o(MCrs),
- //媒体无关接口模块
- .mdc_i(Mdc_O), .md_io(Mdio_IO),
- .phy_log(phy_log_file_desc)
- );
-
- // 连接主机模块
- integer host_log_file_desc;
- WB_MASTER_BEHAVIORAL wb_master(
- .CLK_I(wb_clk),
- .RST_I(wb_rst),
- .TAG_I({`WB_TAG_WIDTH{1'b0}}),
- .TAG_O(),
- .ACK_I(eth_sl_wb_ack_o),
- .ADR_O(eth_sl_wb_adr), // only eth_sl_wb_adr_i[11:2] used
- .CYC_O(eth_sl_wb_cyc_i),
- .DAT_I(eth_sl_wb_dat_o),
- .DAT_O(eth_sl_wb_dat_i),
- .ERR_I(eth_sl_wb_err_o),
- .RTY_I(1'b0), // inactive (1'b0)
- .SEL_O(eth_sl_wb_sel_i),
- .STB_O(eth_sl_wb_stb_i),
- .WE_O (eth_sl_wb_we_i),
- .CAB_O() // NOT USED for now!
- )
- assign eth_sl_wb_adr_i = {20'h0, eth_sl_wb_adr[11:2], 2'h0};
- ……
- //初始化
- initial
- begin
- //复位信号
- wb_rst = 1'b1;
- #423 wb_rst = 1'b0;
- //清除存储器内容
- clear_memories;
- clear_buffer_descriptors;
- #423 StartTB = 1'b1;
- end
- //产生时钟信号
- initial
- begin
- wb_clk=0;
- forever #15 wb_clk = ~wb_clk; // 2*10 ns -> 33.3 MHz
- end
- integer tests_successfull;
- integer tests_failed;
- reg [799:0] test_name; // used for tb_log_file
- reg [3:0] wbm_init_waits; // initial wait cycles between CYC_O and STB_O of WB Master
- reg [3:0] wbm_subseq_waits; // subsequent wait cycles between STB_Os of WB Master
- reg [2:0] wbs_waits; // wait cycles befor WB Slave responds
- reg [7:0] wbs_retries; // if RTY response, then this is the number of retries before ACK
- reg wbm_working; // tasks wbm_write and wbm_read set signal when working and resetit when stop working
- //开始测试内容
- initial
- begin
- wait(StartTB); // 开始测试
- //初始化全局变量
- tests_successfull = 0;
- tests_failed = 0;
- wbm_working = 0;
- wbm_init_waits = 4'h1;
- wbm_subseq_waits = 4'h3;
- wbs_waits = 4'h1;
- wbs_retries = 8'h2;
- wb_slave.cycle_response(`ACK_RESPONSE, wbs_waits, wbs_retries);
- //测试的各个任务
- test_note("PHY generates ideal Carrier sense and Collision signals for following tests");
- eth_phy.carrier_sense_real_delay(0);
- test_mac_full_duplex_transmit(0, 21); //测试全双工方式下传输数据
- test_mac_full_duplex_receive(0, 13); //测试全双工方式下接收数据
- test_mac_full_duplex_flow_control(0, 4); // 测试整个数据流程
- test_note("PHY generates 'real delayed' Carrier sense and Collision signals for following
- tests");
- eth_phy.carrier_sense_real_delay(1);
- // 结束测试
- test_summary;
- $stop;
- end
复制代码
测试内容通过多个测试任务来执行。限于篇幅,测试任务的内容不一一列出。
4.2 外部 PHY 芯片模拟程序 模拟程序模拟了简化的 LXT971A 芯片(Inter 公司的外部 PHY 芯片)。PHY 芯片通过 MIIM(媒体无关接口管理模块)来连接以太网控制器,因此: • 当以太网控制器向 PHY 芯片模拟程序发送数据时,PHY 芯片模拟程序控制数据按照协议的进行传输; • 当 PHY 芯片向以太网控制器发送数据时,外部 PHY 芯片模拟程序首先按照协议要求产生需要传输的数据,然后发送到以太网控制器。
外部 PHY 芯片模拟程序的主要代码如下: 复制代码
4.3 仿真结果 如图 13 所示是对全双工方式下传输数据的测试,图中加亮的 MTxD 是以太网控制器的数据输出。
图 13 全双工模式下发送数据的测试结果
如图 14 所示的是全双工模式下接收数据的测试,图中加亮的 MRxD 是以太网控制器接收数据的输入。
图 14 全双工模式下接收数据的测试结果
如图 15 所示的是全双工模式下数据发送和接收整个过程的测试结果,图中加亮的 MTxD和 MRxD 是以太网控制器发送数据和接收数据的输出和输入端口。
图 15 全双模式下数据发送和接收全过程的测试结果
如图 16 所示的是半双工模式下发送和接收数据全过程的测试结果。
图16 半双工模式下发送和接收数据全过程的测试结果
五、总结
本篇介绍了一个以太网控制器(MAC)的实例。首先介绍了以太网的基本原理,然后介绍了以太网控制器程序的主要结构和主要功能模块的实现过程。最后用一个测试程序验证程序的功能是否满足要求。本章为读者设计自己的以太网控制器提供了一个有用的方案,并且有助于加深对以太网协议的理解。
|