浅谈FPGA网络PHY芯片RTL8211FD的配置和简单使用
最近迷上了FPGA的网络通信和GTP光通信,个人感觉光通信简单一些,那就从难得网络通信开始吧,先搞个最简单的,使用MDIO配置和读取网络PHY的信息。
板子:米联客的MA703FA(A7-35T板子);
参考例程:正点原子达芬奇开发板例程;
IDE:vivado2020.2;
具体的原理啥的建议去看正点原子的文档吧,讲得很好,但原子的例程感觉不贴近实际项目,所以我改了一下,使之适合真是项目。
先来看看这块芯片RTL8211FD的数据手册。
这是官方给的应用架构,很简单,RTL8211FD与MAC通信,通过MDC和MDIO配置。
芯片BD,没啥好说的,典型的rgmii接口,内部模块电路感觉没必要深究,反正也不懂,能用就行了。
硬件复位这里要注意,官方说应至少保持10ms低电平,最好还是按官方说的做,菜就要多听话。
芯片地址这里注意了,RTL8211FD器件地址由5位构成,高两位固定为2’b00,第三位后这三个引脚的上下拉电平决定,所以看看米联客板子的原理图:
由此可得:此RTL8211FD器件地址为:5’b00001;
这块芯片是不需要配置也能使用的,参考官方给的寄存器默认值可知,默认状态下芯片开启自协商,速率1000M,这就行了,已经不需要配置了,但为了学习,我们可以读状态寄存器,从而获得连接状态和通信速率。需要读两个状态寄存器:
第一是这个,需要读他的第2和第5位,
第2位:芯片和MAC连接成功;
第5位:芯片和MAC自协商成功;
还有这个寄存器,主要读芯片的通信速率;
第4到5位是通信速率;
好了,在看代码:
MDIO控制器直接用原子的,写法和他们家的iic控制器类似,够骚,也够繁琐,但能用,菜就别讲究了吧。
//****************************************************************************************//module mdio_dri #(parameter PHY_ADDR = 5'b00001,//PHY地址parameter CLK_DIV = 6'd10 //分频系数)(input clk , //时钟信号input rst_n , //复位信号,低电平有效input i_op_exec , //触发开始信号input i_op_rh_wl , //低电平写,高电平读input [4:0] i_op_addr , //寄存器地址input [15:0] i_op_wr_data, //写入寄存器的数据output reg o_op_done , //读写完成output reg [15:0] o_op_rd_data, //读出的数据output reg o_op_rd_ack , //读应答信号 0:应答 1:未应答output reg o_dri_clk , //驱动时钟 output reg o_eth_mdc , //PHY管理接口的时钟信号inout i_eth_mdio //PHY管理接口的双向数据信号);//parameter define
localparam st_idle = 6'b00_0001; //空闲状态
localparam st_pre = 6'b00_0010; //发送PRE(前导码)
localparam st_start = 6'b00_0100; //开始状态,发送ST(开始)+OP(操作码)
localparam st_addr = 6'b00_1000; //写地址,发送PHY地址+寄存器地址
localparam st_wr_data = 6'b01_0000; //TA+写数据
localparam st_rd_data = 6'b10_0000; //TA+读数据//reg define
reg [5:0] cur_state ;
reg [5:0] next_state;reg [5:0] clk_cnt ; //分频计数
reg [15:0] wr_data_t ; //缓存写寄存器的数据
reg [4:0] addr_t ; //缓存寄存器地址
reg [6:0] cnt ; //计数器
reg st_done ; //状态开始跳转信号
reg [1:0] op_code ; //操作码 2'b01(写) 2'b10(读)
reg mdio_dir ; //MDIO数据(SDA)方向控制
reg mdio_out ; //MDIO输出信号
reg [15:0] rd_data_t ; //缓存读寄存器数据//wire define
wire mdio_in ; //MDIO数据输入
wire [5:0] clk_divide ; //PHY_CLK的分频系数assign i_eth_mdio = mdio_dir ? mdio_out : 1'bz; //控制双向io方向
assign mdio_in = i_eth_mdio; //MDIO数据输入
//将PHY_CLK的分频系数除以2,得到dri_clk的分频系数,方便对MDC和MDIO信号操作
assign clk_divide = CLK_DIV >> 1;//分频得到dri_clk时钟
always @(posedge clk) beginif(!rst_n) begino_dri_clk <= 1'b0;clk_cnt <= 1'b0;endelse if(clk_cnt == clk_divide[5:1] - 1'd1) beginclk_cnt <= 1'b0;o_dri_clk <= ~o_dri_clk;endelse clk_cnt <= clk_cnt + 1'b1;
end//产生PHY_MDC时钟
always @(posedge o_dri_clk) beginif(!rst_n) o_eth_mdc <= 1'b1;else if(cnt[0] == 1'b0) o_eth_mdc <= 1'b1;else o_eth_mdc <= 1'b0;
end//(三段式状态机)同步时序描述状态转移
always @(posedge o_dri_clk) beginif(!rst_n) cur_state <= st_idle;else cur_state <= next_state;
end //组合逻辑判断状态转移条件
always @(*) beginnext_state = st_idle;case(cur_state)st_idle : beginif(i_op_exec) next_state = st_pre;else next_state = st_idle; end st_pre : begin //发送前导码,32'hffffffffif(st_done) next_state = st_start;else next_state = st_pre;endst_start : begin //ST+OP=2'b01+2'bxxif(st_done) next_state = st_addr;else next_state = st_start;endst_addr : begin //PHYAD+REGADif(st_done) beginif(op_code == 2'b01) next_state = st_wr_data; //MDIO接口写操作 else next_state = st_rd_data; //MDIO接口读操作 endelse next_state = st_addr; endst_wr_data : beginif(st_done) next_state = st_idle;else next_state = st_wr_data;end st_rd_data : beginif(st_done) next_state = st_idle;else next_state = st_rd_data;end default : next_state = st_idle;endcaseend//时序电路描述状态输出
always @(posedge o_dri_clk) beginif(!rst_n) begincnt <= 5'd0;op_code <= 1'b0;addr_t <= 1'b0;wr_data_t <= 1'b0;rd_data_t <= 1'b0;o_op_done <= 1'b0;st_done <= 1'b0; o_op_rd_data <= 1'b0;o_op_rd_ack <= 1'b1;mdio_dir <= 1'b0;mdio_out <= 1'b1;endelse beginst_done <= 1'b0 ; cnt <= cnt +1'b1 ; case(cur_state)st_idle : beginmdio_out <= 1'b1; mdio_dir <= 1'b0; o_op_done <= 1'b0; cnt <= 7'b0; if(i_op_exec) beginop_code <= {i_op_rh_wl,~i_op_rh_wl}; //OP_CODE: 2'b01(写) 2'b10(读) addr_t <= i_op_addr;wr_data_t <= i_op_wr_data;o_op_rd_ack <= 1'b1;end end st_pre : begin //发送前导码:32个1bit mdio_dir <= 1'b1; //切换MDIO引脚方向:输出mdio_out <= 1'b1; //MDIO引脚输出高电平if(cnt == 7'd62) st_done <= 1'b1;else if(cnt == 7'd63) cnt <= 7'b0;end st_start : begincase(cnt)7'd1 : mdio_out <= 1'b0; //发送开始信号 2'b017'd3 : mdio_out <= 1'b1; 7'd5 : mdio_out <= op_code[1]; //发送操作码7'd6 : st_done <= 1'b1;7'd7 : beginmdio_out <= op_code[0];cnt <= 7'b0; end default : ;endcaseend st_addr : begincase(cnt)7'd1 : mdio_out <= PHY_ADDR[4]; //发送PHY地址7'd3 : mdio_out <= PHY_ADDR[3];7'd5 : mdio_out <= PHY_ADDR[2];7'd7 : mdio_out <= PHY_ADDR[1]; 7'd9 : mdio_out <= PHY_ADDR[0];7'd11: mdio_out <= addr_t[4]; //发送寄存器地址7'd13: mdio_out <= addr_t[3];7'd15: mdio_out <= addr_t[2];7'd17: mdio_out <= addr_t[1]; 7'd18: st_done <= 1'b1;7'd19: beginmdio_out <= addr_t[0]; cnt <= 7'd0;end default : ;endcase end st_wr_data : begincase(cnt)7'd1 : mdio_out <= 1'b1; //发送TA,写操作(2'b10)7'd3 : mdio_out <= 1'b0;7'd5 : mdio_out <= wr_data_t[15];//发送写寄存器数据7'd7 : mdio_out <= wr_data_t[14];7'd9 : mdio_out <= wr_data_t[13];7'd11: mdio_out <= wr_data_t[12];7'd13: mdio_out <= wr_data_t[11];7'd15: mdio_out <= wr_data_t[10];7'd17: mdio_out <= wr_data_t[9];7'd19: mdio_out <= wr_data_t[8];7'd21: mdio_out <= wr_data_t[7];7'd23: mdio_out <= wr_data_t[6];7'd25: mdio_out <= wr_data_t[5];7'd27: mdio_out <= wr_data_t[4];7'd29: mdio_out <= wr_data_t[3];7'd31: mdio_out <= wr_data_t[2];7'd33: mdio_out <= wr_data_t[1];7'd35: mdio_out <= wr_data_t[0];7'd37: beginmdio_dir <= 1'b0;mdio_out <= 1'b1;end7'd39: st_done <= 1'b1; 7'd40: begincnt <= 7'b0;o_op_done <= 1'b1; //写操作完成,拉高op_done信号 end default : ;endcase endst_rd_data : begincase(cnt)7'd1 : beginmdio_dir <= 1'b0; //MDIO引脚切换至输入状态mdio_out <= 1'b1;end7'd2 : ; //TA[1]位,该位为高阻状态,不操作 7'd4 : o_op_rd_ack <= mdio_in; //TA[0]位,0(应答) 1(未应答)7'd6 : rd_data_t[15] <= mdio_in; //接收寄存器数据7'd8 : rd_data_t[14] <= mdio_in;7'd10: rd_data_t[13] <= mdio_in;7'd12: rd_data_t[12] <= mdio_in;7'd14: rd_data_t[11] <= mdio_in;7'd16: rd_data_t[10] <= mdio_in;7'd18: rd_data_t[9] <= mdio_in;7'd20: rd_data_t[8] <= mdio_in;7'd22: rd_data_t[7] <= mdio_in;7'd24: rd_data_t[6] <= mdio_in;7'd26: rd_data_t[5] <= mdio_in;7'd28: rd_data_t[4] <= mdio_in;7'd30: rd_data_t[3] <= mdio_in;7'd32: rd_data_t[2] <= mdio_in;7'd34: rd_data_t[1] <= mdio_in;7'd36: rd_data_t[0] <= mdio_in;7'd39: st_done <= 1'b1;7'd40: begino_op_done <= 1'b1; //读操作完成,拉高op_done信号 o_op_rd_data <= rd_data_t;rd_data_t <= 16'd0;cnt <= 7'd0;enddefault : ;endcase end default : ;endcase end
end endmodule
然后是控制逻辑,原子的例程是通过触摸按键软复位RTL8211FD,然后再读那两个寄存器,感觉没啥用,自己重写了一个,功能是硬件复位完成后直接循环读那两个寄存器,如果自协商完成且连接成功,led亮,否则灭,再用另个led来只是当前的通信速率,直接上代码:
//****************************************************************************************//
module mdio_ctrl(
(* mark_debug ="true" *) input clk ,
(* mark_debug ="true" *) input rst_n ,(* mark_debug ="true" *) input i_op_done , //读写完成
(* mark_debug ="true" *) input [15:0] i_op_rd_data , //读出的数据
(* mark_debug ="true" *) input i_op_rd_ack , //读应答信号 0:应答 1:未应答
(* mark_debug ="true" *) output reg o_op_exec , //触发开始信号
(* mark_debug ="true" *) output reg o_op_rh_wl , //低电平写,高电平读
(* mark_debug ="true" *) output reg [4:0] o_op_addr , //寄存器地址(* mark_debug ="true" *) input i_eth_rst_n ,
(* mark_debug ="true" *) output reg o_link_led ,
(* mark_debug ="true" *) output reg [1:0] o_speed_led );(* mark_debug ="true" *) reg [2:0] flow_cnt; //流程控制计数器 always @(posedge clk) beginif(!rst_n) beginflow_cnt <= 3'd0;
// link_error <= 1'b0;o_op_exec <= 1'b0; o_op_rh_wl <= 1'b0; o_op_addr <= 1'b0;
// op_wr_data <= 1'b0;o_link_led <= 1'b0;o_speed_led<= 2'b0;endelse begincase(flow_cnt)3'd0:beginif(i_eth_rst_n) flow_cnt <= 3'd1; //reste_n is okend3'd1: begino_op_exec <= 1'b1; //开始操作MDIOo_op_rh_wl<= 1'b1; //读操作o_op_addr <= 5'h01; //读BMSR状态寄存器flow_cnt<= 3'd2;end3'd2: beginif(i_op_done) begin //读操作完成if(!i_op_rd_ack) flow_cnt<= 3'd3; //phy应答,做后续判断else flow_cnt<= 3'd1; //phy未应答,重新发起读BMSR状态寄存器 endend3'd3: beginif(i_op_rd_data[5] == 1'b1 && i_op_rd_data[2] == 1'b1) begino_link_led<=1'b1; //自协商完成,link完成flow_cnt<=3'd4;endelse o_link_led<=1'b0; end3'd4: begin //读PHYSR特定状态寄存器o_op_addr <= 5'h1A;flow_cnt<= 3'd5; end3'd5: beginif(i_op_done) begin //读操作完成if(!i_op_rd_ack) flow_cnt<= 3'd6; //phy应答,做后续判断else flow_cnt<= 3'd4; //phy未应答,重新发起读PHYSR特定状态寄存器endend 3'd6: beginflow_cnt<=3'd1;if(i_op_rd_data[5:4] == 2'b10) o_speed_led<=2'b11; //1000Mbpselse if(i_op_rd_data[5:4] == 2'b01) o_speed_led<=2'b01; //100Mbpselse if(i_op_rd_data[5:4] == 2'b00) o_speed_led<=2'b10; //100Mbpselse o_speed_led<= 2'b0; enddefault: flow_cnt <= 3'd0;endcaseend
endendmodule
最后是顶层,这里加了硬件复位的10ms,之前说了,菜就别嫌麻烦,跟官方走:
module mdio_rw_test(input sys_clk ,output eth_mdc , //PHY管理接口的时钟信号inout eth_mdio , //PHY管理接口的双向数据信号output eth_rst_n , //以太网复位信号output o_link_led ,output [1:0] o_speed_led //LED连接速率指示);//wire define
wire op_exec ; //触发开始信号
wire op_rh_wl ; //低电平写,高电平读
wire [4:0] op_addr ; //寄存器地址
wire [15:0] op_wr_data ; //写入寄存器的数据
wire op_done ; //读写完成
wire [15:0] op_rd_data ; //读出的数据
wire op_rd_ack ; //读应答信号 0:应答 1:未应答
wire dri_clk ; //驱动时钟
wire rsn_n;
wire clk_50m;clk_wiz_0 u_clk_wiz_0(// Clock out ports.clk_out1(clk_50m), // output clk_out1// Status and control signals.locked(rsn_n), // output locked// Clock in ports.clk_in1(sys_clk)); // input clk_in1localparam T_10MS = 10_000_000; //10ms=10_000_000ns
localparam T_P = 20 ; //50M周期=20ns
localparam RST_CNT= T_10MS/T_P+10;
reg [18:0] eth_rsrn_cnt;
reg o_eth_rst_n;
//根据数据手册,硬件复位至少10ms的低电平
always @(posedge clk_50m) beginif(!rsn_n) eth_rsrn_cnt<='d0;else if(eth_rsrn_cnt==RST_CNT) eth_rsrn_cnt<=RST_CNT;else eth_rsrn_cnt<=eth_rsrn_cnt+19'd1;
endalways @(posedge clk_50m) beginif(!rsn_n) o_eth_rst_n<='d0;else if(eth_rsrn_cnt==RST_CNT) o_eth_rst_n<=1'd1;else o_eth_rst_n<=1'd0;
end//硬件复位
assign eth_rst_n = o_eth_rst_n;//MDIO接口驱动
mdio_dri #(.PHY_ADDR (5'h01), //PHY地址.CLK_DIV (6'd10) //分频系数)u_mdio_dri(.clk (clk_50m ),.rst_n (rsn_n ),.i_op_exec (op_exec ),.i_op_rh_wl (op_rh_wl ), .i_op_addr (op_addr ), .i_op_wr_data(op_wr_data), .o_op_done (op_done ), .o_op_rd_data(op_rd_data), .o_op_rd_ack (op_rd_ack ), .o_dri_clk (dri_clk ), .o_eth_mdc (eth_mdc ), .i_eth_mdio (eth_mdio )
); mdio_ctrl u_mdio_ctrl(.clk (dri_clk ),.rst_n (rsn_n ),.i_op_done (op_done ), //读写完成.i_op_rd_data (op_rd_data ), //读出的数据.i_op_rd_ack (op_rd_ack ), //读应答信号 0:应答 1:未应答.o_op_exec (op_exec ), //触发开始信号.o_op_rh_wl (op_rh_wl ), //低电平写,高电平读.o_op_addr (op_addr ), //寄存器地址.i_eth_rst_n (o_eth_rst_n),.o_link_led (o_link_led ),.o_speed_led (o_speed_led));
endmodule
结果:灯亮,成功
浅谈FPGA网络PHY芯片RTL8211FD的配置和简单使用相关推荐
- FPGA基于SFP光口实现1G千兆网UDP通信 1G/2.5G Ethernet PCS/PMA or SGMII替代网络PHY芯片 提供工程源码和技术支持
目录 1.前言 2.我这里已有的UDP方案 3.详细设计方案 4.vivado工程详解 5.上板调试验证并演示 6.福利:工程代码的获取 1.前言 目前网上的fpga实现udp基本生态如下: 1:ve ...
- FPGA基于SFP光口实现10G万兆网UDP通信 10G Ethernet Subsystem替代网络PHY芯片 提供工程源码和技术支持
目录 1.前言 2.我这里已有的UDP方案 3.详细设计方案 4.vivado工程详解 5.上板调试验证并演示 6.福利:工程代码的获取 1.前言 目前网上的fpga实现udp基本生态如下: 1:ve ...
- 千兆网络PHY芯片 RTL8211E的实践应用(自我总结篇)
这篇算是对FPGA做千兆以太网的电路搭建这一块儿的最全的扫盲篇了吧,废话不多说,直接上干货. 一.千兆以太网的系统搭建 1.硬件系统搭建 以太网MAC模块负责实现以太网MAC子层的功能,完成802.3 ...
- 浅谈软件定义网络(SDN)技术研究现状和发展趋势
浅谈软件定义网络(SDN)技术研究现状和发展趋势 友情全文PDF链接:浅谈软件定义网络(SDN)技术研究现状和发展趋势.pdf-网络基础文档类资源-CSDN下载 ...
- 浅谈Java网络编程之Socket (2)
<浅谈Java网络编程之Socket (1)>中我们已经和大家说到客户端的网络编程,下面和大家分享的是服务器的实现代码. import java.net.*; import java.io ...
- 浅谈Openstack网络原理(openstack无法上网?)
浅谈Openstack网络 本版本为Stein版本,采用OVS网络 测试环境:1控制节点,2个计算节点 目录 浅谈Openstack网络 一.网络结构图 二.OVS分析 三.DHCP服务 四.Rout ...
- 一文读懂如何使用FPGA驱动PHY芯片
这里写自定义目录标题 如何使用FPGA驱动PHY芯片 前言 必要的硬件知识 如何确定PHY芯片的物理地址? 如何确定PHY芯片的工作模式? 如何驱动PHY芯片? MDIO的通讯协议是什么? PHY芯片 ...
- 浅谈软件定义网络SDN
浅谈软件定义网络SDN 前言 学习主要内容 一.SDN简介 二.SDN的三个主要特征 转控分离 集中控制 开放接口 三.SDN的工作原理 SDN网络架构的三层模型 SDN网络架构下的三个接口 SDN基 ...
- 校园服务器系统的意义,浅谈校园网络建设的意义与作用.doc
PAGE PAGE 7 浅谈校园网络建设的意义和作用 摘要:本文围绕校园网建设的意义和作用.结合本校校园网建设的基本经验,运用大量具体实例阐述大力建设校园网络的必要性和可行性,供大家借鉴. 关键字:校 ...
最新文章
- 计算机硬盘登记表,硬盘固件的 P 表与 G 表
- activex for chrome扩展程序 下载”_chrome系列-扩展程序开发学习-从无到有
- 欢迎光临CAX软件二次开发开源社区!
- matlab pca降维_手撸PCA(Python七行代码实现)
- CSS3 多列布局的column-gap 和 column-rule属性
- [素数拓展] 质因数的个数 [2007年清华大学计算机研究生机试真题]
- Android ViewPropertyAnimator:让动画变得简单起来!
- cocoapods 总结
- php format tool,usb 開機碟製作工具HP USB Disk Storage format Tool 2.23
- wordpress网站提示“建立数据库连接时出错”
- java-房屋出租系统
- 网站被封申诉通道方式方法
- SCUT校赛130:对抗女巫的魔法碎片(思维)
- HDU1757(矩阵快速幂+简单的矩阵构造)
- UG 信息窗口弹不出来 测量 长度 角度 信息 窗口 弹不出来
- 何水无鱼?何山无石?何人无父?何女无夫?何树无枝?何城无市?
- 小程序前端获取手机号码
- 严格模式和普通模式的区别
- cf Round#779 D 388535
- elementui下拉选择框