当前位置:   article > 正文

FPGA千兆网口数据传输MDIO接口——FPGA学习笔记3_phy的mdio和mdc可以随便接到fpga的管脚吗

phy的mdio和mdc可以随便接到fpga的管脚吗

一、以太网简介

        是当今现有局域网采用的最通用的通信协议标准,它规定了包括物理层的连线、电子信号和介质访问层协议的内容。成本低,通信速率高,抗干扰能力强。

        1、分类

标准以太网:10Mbit/s

快速以太网:100Mbit/s

千兆以太网:1000Mbit/s

.........

以太网和千兆网口其实不完全相同。以太网是一种局域网技术标准,而千兆网口通常指的是支持1Gb/s(即千兆位每秒)速度的网络接口。

以太网是一种通用的局域网技术标准,定义了数据传输的规范和硬件设备之间的通信方式。而千兆网口是一种物理接口标准,用于连接计算机或其他网络设备到网络中,支持更高的数据传输速度。

因此,可以说千兆网口是以太网的一种实现方式,它提供了更快的数据传输速度,但并不等同于以太网。在实际应用中,通常会使用以太网技术,并通过千兆网口进行连接,以实现更快的网络通信速度。

        2、以太网接口

        以太网通信离不开连接端口的支持,网络数据连接的端口就是以太网接口。以太网接口类型有RJ45接口,RJ11接口(电话线接口),SC光纤接口等其中RJ45接口是我们现在最常见的网络设备接口(如:电脑网口)

3、6一对是为了向下兼容10/100M

上图为RGMII接口,时钟为双沿2.5MHz、25MHz、250MHz。位宽为4位。(节省引脚)

GMII接口,时钟为单沿1.25MHz、12.5MHz、125MHz。位宽为8位。

MAC侧完成对报文的封包和解包过程

PHY芯片:实现模数转换

ETH_RXC接收端时钟
ETH_RXCTL接收端数据使能(接受报文有效,该引脚拉高)
ETH_RXD[3:0]接收数据
ETH_TXC发送端时钟
ETH_TXCTL发送端数据使能(发送报文有效,该引脚拉高)
ETH_TXD[3:0]发送数据
ETH_RST_N复位脚
ETH_MDC
ETH_MDIO

MDIO接口:(默认即可实现功能)

        3、UDP网络协议简介

UDP(User Datagram Protocol,用户数据报协议)是一种无连接的网络传输协议,位于OSI模型的传输层。以下是UDP网络协议的简介:

                (1)无连接性:UDP是一种无连接的协议,发送端在发送数据之前不需要与接收端建立连接,也不会维护连接状态。

                (2)面向数据报:UDP以数据报(Datagram)的形式传输数据,每个数据报都是独立的,互相之间没有关联。

                (3)不可靠性:UDP不提供数据可靠性保证,数据报可能会丢失、重复或无序到达。也不会进行数据校验和重传。

                (4)高效性:由于没有连接建立和维护的开销,以及简化的功能,UDP比TCP更轻量级,传输效率更高。

                (5)适用场景:UDP适用于对实时性要求高、数据传输完整性要求不高的应用场景,如音频、视频流媒体传输、在线游戏等。

                总的来说,UDP是一种简单、高效但不可靠的网络传输协议,适用于对实时性要求高、能容忍少量数据丢失的应用场景。

        4、数据链路

        数据通过FPGA进行数据协议的打包和MAC协议处理,通过RGMII总线协议传输将数据传送至PHY芯片,PHY芯片将数据进行处理后发送至RJ45接口进行数据发送。

        物理层(RJ45网口接口)、数据链路层(PHY芯片和MAC协议)、网络层(在FPGA中处理的MAC协议)、传输层(在FPGA中处理的数据协议打包,用UDP或者TCP协议就是说的这一层)

二、以太网PHY芯片简介RTL8211

        1、PHY芯片:物理层芯片(物理接口收发器)

                自动协商通信速率,或自行配置

        2、BMCI寄存器配置(RTL8211为例)

                BMCI寄存器addr为:0x00

                (1)0.15:软件复位,1复位,0工作。结束复位后,无需手动写0,结束复位会自动归0,

                (2)0.14:环回,主要用于检测,1使能,0失能。

                (3)0.13,0.6:Speed配置,配置不同速率(先失能自协商) 

                (4)0.12:自协商配置,默认使能,1使能,0失能。

                (5)0.11:掉电模式配置,1掉电模式,0正常工作模式。

                (6)0.10:隔离配置,RGMII接口隔离,引脚失能,只有MDIO,MDC配置有效

                (7)0.9:重新开始自协商配置。

                (8)0.8:双工模式配置

                (9)0.7:冲突测试

                (10)0.5:单向使能:1双方建立连接成功后通信,0直接通信

        3、BMSR寄存器配置

                BMCI寄存器addr为:0x01

                        (1)1.5:自协商完成

                        (2)1.2:是否连接OK

        4、PHYSR寄存器配置

                BMCI寄存器addr为:0x11

                (1)17.15:14  :连接速率(前提link ok,自协商完成)

三、以太网PHY芯片简介YT8531

        1、PHY芯片

网线差分模拟信号转换数字信号,FPGA数字信号转换网线差分模拟信号。

        2、引脚复用

        3、硬件原理图(正点原子达芬奇开发板)

22R电阻做阻抗匹配。

        4、PHY地址接口

一定会响应0x00!!

        5、基本控制寄存器配置

寄存器地址0x00

                (1) 15:软复位,带自清0,默认为0 。0工作,1复位。

                (2) 14:内部环回,默认为0。0失能,1使能。

                (3) 13:速率配置低位,关闭自协商才可以使用,bit6,13进行配置。

                (4) 12:自协商使能,默认支持。1使能,0失能。

                (5) 11:掉电模式,1掉电,0正常。

                (6) 10:隔离模式,一般为0正常模式

                (7) 9:重新自协商,自带置1。

                (8) 8:配置双工,关闭自协商才可以使用,1双工,0半双工。

        6、基本状态寄存器配置 

寄存器地址0x01

                (1) 13:双工模式检测。        0:半双工,         1:全双工。

                (2) 15~14:传输速度等级检测。10:1000M      01:100M       00:10M

                (3) 2:连接状态。      1:连接已建立,        0:链接未建立。

                (4) 3:自协商能力,    1具有,      0不具备。

                (5) 4:远端错误指示位:读取后自动清0         0:远端设备正常    1:远端设备异常

                (6) 5:自协商完成标志,软复位后自动清0       1:完成自协商      0:未完成自协商。

                (7) 6:前导码模式,1:不检测前导码直接接受        0:检测前导码

                (8) 7:单项传输能力:0:当检测到建立有效连接才开始传输     1:直接开始传输。

        7、特定状态寄存器

寄存器地址0x11

四、MDIO接口时序

        MDIO: Management Data Input/Output

        也被称为SMI ( Serial Management Interface ) ,即串行管理接口。

        MAC和 PHY芯片有一个配置接口,即MDIO接口,可以配置 PHY芯片的工作模式以及获取 PHY芯片的若干状态信息。 

        1、MDIO接口连接图

        2、PHY帧格式

        管理接口通过MDC和MDIO引脚提供对内部寄存器的访问,如IEEE802.3u第22节所述。MDC信号由MAC提供,是MDIO信号的管理数据时钟参考。MDIO是管理数据的输入/输出,是一个双向信号,与MDC同步运行。MDIO引脚需要一个1.5k欧姆的上拉电阻,以在空闲和周转期间保持MDIO高电平。

前置抑制为RTL8211E/RTL8211EG上电后的默认设置。然而,在操作之间仍然必须至少有一个空闲位。RTL8211E/RTL8211EG可以共享同一条MDIO线。在交换机/路由器应用程序中,每个端口应该在硬件复位序列中分配一个唯一的地址,并且只能通过该唯一的PHY地址进行寻址。有关RTL8211E/RTL8211EG管理寄存器的详细信息,请参见第30页第8节寄存器描述。

写状态下,一直由FPGA输出数据给到PHY芯片;读状态下,在TA位切换方向

                (1)Preamble:前导码,32个逻辑1,同时给出时钟。

                (2)ST:帧开始。固定01

                (3)OP:操作码,指示当前读写

                (4)PHYAD:PHY地址

                (5)REGAD:寄存器地址

                (6)TA:2bit宽度,时间间隔,切换FPGA数据线方向。Z为高阻状态,0为应答。

                (7)DATA:数据 

        3、PHY读写传输时序

                        上升沿采集数据,下降沿更新数据;MDC的最小周期为80ns对应时钟最大为12.5MHz 

 

五、MDIO读写测试实验

        PHY芯片不进行配置也可以使用 

        1、系统框图

        2、模块原理图

u_mdio_dri模块:

op_addr:读写地址

op_exec:触发信号↓↓↓↓↓↓↓↓↓↓↓

op_rh_wl:读写标志信号    1读    0写

op_wr_data:具体写入数据

op_done:读写结束后,拉高该信号

op_rd_ack:读应答信号    0应答     1未应答(帧格式里面的AT)

op_rd_data:读出的信号

dri_clk:驱动时钟

 当op_exec信号拉高标志一次读写操作开始,根据op_rh_wl状态判断读写操作,同时给出读写操作地址op_addr(如果是写操作要对应给出op_wr_data),读写操作结束后,拉高op_done信号,表示当前读写操作完成,控制模块可以发起下一次操作(如果是写读操作,在op_done为高期间,如果是0表示应答,如果是1表示未应答)(如果读操作,在op_done为高期间,会返回读到的数据)。

        3、状态机

        4、用户接口时序

        5、程序设计

                (1)mdio_dri模块

  1. module mdio_dri #(
  2. parameter PHY_ADDR = 5'b00100,//PHY地址
  3. parameter CLK_DIV = 6'd10 //分频系数
  4. )
  5. (
  6. input clk , //时钟信号
  7. input rst_n , //复位信号,低电平有效
  8. input op_exec , //触发开始信号
  9. input op_rh_wl , //低电平写,高电平读
  10. input [4:0] op_addr , //寄存器地址
  11. input [15:0] op_wr_data, //写入寄存器的数据
  12. output reg op_done , //读写完成
  13. output reg [15:0] op_rd_data, //读出的数据
  14. output reg op_rd_ack , //读应答信号 0:应答 1:未应答
  15. output reg dri_clk , //驱动时钟
  16. output reg eth_mdc , //PHY管理接口的时钟信号
  17. inout eth_mdio //PHY管理接口的双向数据信号
  18. );
  19. //parameter define
  20. localparam st_idle = 6'b00_0001; //空闲状态
  21. localparam st_pre = 6'b00_0010; //发送PRE(前导码)
  22. localparam st_start = 6'b00_0100; //开始状态,发送ST(开始)+OP(操作码)
  23. localparam st_addr = 6'b00_1000; //写地址,发送PHY地址+寄存器地址
  24. localparam st_wr_data = 6'b01_0000; //TA+写数据
  25. localparam st_rd_data = 6'b10_0000; //TA+读数据
  26. //reg define
  27. reg [5:0] cur_state ;
  28. reg [5:0] next_state;
  29. reg [5:0] clk_cnt ; //分频计数
  30. reg [15:0] wr_data_t ; //缓存写寄存器的数据
  31. reg [4:0] addr_t ; //缓存寄存器地址
  32. reg [6:0] cnt ; //计数器
  33. reg st_done ; //状态开始跳转信号
  34. reg [1:0] op_code ; //操作码 2'b01(写) 2'b10(读)
  35. reg mdio_dir ; //MDIO数据(SDA)方向控制
  36. reg mdio_out ; //MDIO输出信号
  37. reg [15:0] rd_data_t ; //缓存读寄存器数据
  38. //wire define
  39. wire mdio_in ; //MDIO数据输入
  40. wire [5:0] clk_divide ; //PHY_CLK的分频系数
  41. assign eth_mdio = mdio_dir ? mdio_out : 1'bz; //控制双向io方向
  42. assign mdio_in = eth_mdio; //MDIO数据输入
  43. //将PHY_CLK的分频系数除以2,得到dri_clk的分频系数,方便对MDC和MDIO信号操作
  44. assign clk_divide = CLK_DIV >> 1;
  45. //分频得到dri_clk时钟
  46. always @(posedge clk or negedge rst_n) begin
  47. if(!rst_n) begin
  48. dri_clk <= 1'b0;
  49. clk_cnt <= 1'b0;
  50. end
  51. else if(clk_cnt == clk_divide[5:1] - 1'd1) begin
  52. clk_cnt <= 1'b0;
  53. dri_clk <= ~dri_clk;
  54. end
  55. else
  56. clk_cnt <= clk_cnt + 1'b1;
  57. end
  58. //产生PHY_MDC时钟
  59. always @(posedge dri_clk or negedge rst_n) begin
  60. if(!rst_n)
  61. eth_mdc <= 1'b1;
  62. else if(cnt[0] == 1'b0)
  63. eth_mdc <= 1'b1;
  64. else
  65. eth_mdc <= 1'b0;
  66. end
  67. //(三段式状态机)同步时序描述状态转移
  68. always @(posedge dri_clk or negedge rst_n) begin
  69. if(!rst_n)
  70. cur_state <= st_idle;
  71. else
  72. cur_state <= next_state;
  73. end
  74. //组合逻辑判断状态转移条件
  75. always @(*) begin
  76. next_state = st_idle;
  77. case(cur_state)
  78. st_idle : begin
  79. if(op_exec)
  80. next_state = st_pre;
  81. else
  82. next_state = st_idle;
  83. end
  84. st_pre : begin
  85. if(st_done)
  86. next_state = st_start;
  87. else
  88. next_state = st_pre;
  89. end
  90. st_start : begin
  91. if(st_done)
  92. next_state = st_addr;
  93. else
  94. next_state = st_start;
  95. end
  96. st_addr : begin
  97. if(st_done) begin
  98. if(op_code == 2'b01) //MDIO接口写操作
  99. next_state = st_wr_data;
  100. else
  101. next_state = st_rd_data; //MDIO接口读操作
  102. end
  103. else
  104. next_state = st_addr;
  105. end
  106. st_wr_data : begin
  107. if(st_done)
  108. next_state = st_idle;
  109. else
  110. next_state = st_wr_data;
  111. end
  112. st_rd_data : begin
  113. if(st_done)
  114. next_state = st_idle;
  115. else
  116. next_state = st_rd_data;
  117. end
  118. default : next_state = st_idle;
  119. endcase
  120. end
  121. //时序电路描述状态输出
  122. always @(posedge dri_clk or negedge rst_n) begin
  123. if(!rst_n) begin
  124. cnt <= 5'd0;
  125. op_code <= 1'b0;
  126. addr_t <= 1'b0;
  127. wr_data_t <= 1'b0;
  128. rd_data_t <= 1'b0;
  129. op_done <= 1'b0;
  130. st_done <= 1'b0;
  131. op_rd_data <= 1'b0;
  132. op_rd_ack <= 1'b1;
  133. mdio_dir <= 1'b0;
  134. mdio_out <= 1'b1;
  135. end
  136. else begin
  137. st_done <= 1'b0 ;
  138. cnt <= cnt +1'b1 ;
  139. case(cur_state)
  140. st_idle : begin
  141. mdio_out <= 1'b1;
  142. mdio_dir <= 1'b0;
  143. op_done <= 1'b0;
  144. cnt <= 7'b0;
  145. if(op_exec) begin
  146. op_code <= {op_rh_wl,~op_rh_wl}; //OP_CODE: 2'b01(写) 2'b10(读)
  147. addr_t <= op_addr;
  148. wr_data_t <= op_wr_data;
  149. op_rd_ack <= 1'b1;
  150. end
  151. end
  152. st_pre : begin //发送前导码:32个1bit
  153. mdio_dir <= 1'b1; //切换MDIO引脚方向:输出
  154. mdio_out <= 1'b1; //MDIO引脚输出高电平
  155. if(cnt == 7'd62)
  156. st_done <= 1'b1;
  157. else if(cnt == 7'd63)
  158. cnt <= 7'b0;
  159. end
  160. st_start : begin
  161. case(cnt)
  162. 7'd1 : mdio_out <= 1'b0; //发送开始信号 2'b01
  163. 7'd3 : mdio_out <= 1'b1;
  164. 7'd5 : mdio_out <= op_code[1]; //发送操作码
  165. 7'd6 : st_done <= 1'b1;
  166. 7'd7 : begin
  167. mdio_out <= op_code[0];
  168. cnt <= 7'b0;
  169. end
  170. default : ;
  171. endcase
  172. end
  173. st_addr : begin
  174. case(cnt)
  175. 7'd1 : mdio_out <= PHY_ADDR[4]; //发送PHY地址
  176. 7'd3 : mdio_out <= PHY_ADDR[3];
  177. 7'd5 : mdio_out <= PHY_ADDR[2];
  178. 7'd7 : mdio_out <= PHY_ADDR[1];
  179. 7'd9 : mdio_out <= PHY_ADDR[0];
  180. 7'd11: mdio_out <= addr_t[4]; //发送寄存器地址
  181. 7'd13: mdio_out <= addr_t[3];
  182. 7'd15: mdio_out <= addr_t[2];
  183. 7'd17: mdio_out <= addr_t[1];
  184. 7'd18: st_done <= 1'b1;
  185. 7'd19: begin
  186. mdio_out <= addr_t[0];
  187. cnt <= 7'd0;
  188. end
  189. default : ;
  190. endcase
  191. end
  192. st_wr_data : begin
  193. case(cnt)
  194. 7'd1 : mdio_out <= 1'b1; //发送TA,写操作(2'b10)
  195. 7'd3 : mdio_out <= 1'b0;
  196. 7'd5 : mdio_out <= wr_data_t[15];//发送写寄存器数据
  197. 7'd7 : mdio_out <= wr_data_t[14];
  198. 7'd9 : mdio_out <= wr_data_t[13];
  199. 7'd11: mdio_out <= wr_data_t[12];
  200. 7'd13: mdio_out <= wr_data_t[11];
  201. 7'd15: mdio_out <= wr_data_t[10];
  202. 7'd17: mdio_out <= wr_data_t[9];
  203. 7'd19: mdio_out <= wr_data_t[8];
  204. 7'd21: mdio_out <= wr_data_t[7];
  205. 7'd23: mdio_out <= wr_data_t[6];
  206. 7'd25: mdio_out <= wr_data_t[5];
  207. 7'd27: mdio_out <= wr_data_t[4];
  208. 7'd29: mdio_out <= wr_data_t[3];
  209. 7'd31: mdio_out <= wr_data_t[2];
  210. 7'd33: mdio_out <= wr_data_t[1];
  211. 7'd35: mdio_out <= wr_data_t[0];
  212. 7'd37: begin
  213. mdio_dir <= 1'b0;
  214. mdio_out <= 1'b1;
  215. end
  216. 7'd39: st_done <= 1'b1;
  217. 7'd40: begin
  218. cnt <= 7'b0;
  219. op_done <= 1'b1; //写操作完成,拉高op_done信号
  220. end
  221. default : ;
  222. endcase
  223. end
  224. st_rd_data : begin
  225. case(cnt)
  226. 7'd1 : begin
  227. mdio_dir <= 1'b0; //MDIO引脚切换至输入状态
  228. mdio_out <= 1'b1;
  229. end
  230. 7'd2 : ; //TA[1]位,该位为高阻状态,不操作
  231. 7'd4 : op_rd_ack <= mdio_in; //TA[0]位,0(应答) 1(未应答)
  232. 7'd6 : rd_data_t[15] <= mdio_in; //接收寄存器数据
  233. 7'd8 : rd_data_t[14] <= mdio_in;
  234. 7'd10: rd_data_t[13] <= mdio_in;
  235. 7'd12: rd_data_t[12] <= mdio_in;
  236. 7'd14: rd_data_t[11] <= mdio_in;
  237. 7'd16: rd_data_t[10] <= mdio_in;
  238. 7'd18: rd_data_t[9] <= mdio_in;
  239. 7'd20: rd_data_t[8] <= mdio_in;
  240. 7'd22: rd_data_t[7] <= mdio_in;
  241. 7'd24: rd_data_t[6] <= mdio_in;
  242. 7'd26: rd_data_t[5] <= mdio_in;
  243. 7'd28: rd_data_t[4] <= mdio_in;
  244. 7'd30: rd_data_t[3] <= mdio_in;
  245. 7'd32: rd_data_t[2] <= mdio_in;
  246. 7'd34: rd_data_t[1] <= mdio_in;
  247. 7'd36: rd_data_t[0] <= mdio_in;
  248. 7'd39: st_done <= 1'b1;
  249. 7'd40: begin
  250. op_done <= 1'b1; //读操作完成,拉高op_done信号
  251. op_rd_data <= rd_data_t;
  252. rd_data_t <= 16'd0;
  253. cnt <= 7'd0;
  254. end
  255. default : ;
  256. endcase
  257. end
  258. default : ;
  259. endcase
  260. end
  261. end
  262. endmodule
                        ①控制双向io方向

                        ②分频得到dri_clk时钟

                 

                首先对CLK_DIV进行除2,目的是得到2倍的CLK_DIV时钟分频,再利用clk_cnt计数器产生dri_clk时钟。clk_divide[5:1] - 1'd1     取clk_divide的第5到第1位(实际作用除2取整,记满进行信号反转),再减掉1位作为计数器最大值、dri_clk信号跳转标志。(对时钟信号进行二分频)

                        ③产生PHY_MDC时钟

               cnt计数器最低位,实际只在0.1互相切换, 实际是对dri_clk进行二分频。(也就是dri_clk是MDC的两倍,此时eth_mdc是时钟频率四分频)

                        ④状态机——状态转移
  1. //parameter define
  2. localparam st_idle = 6'b00_0001; //空闲状态
  3. localparam st_pre = 6'b00_0010; //发送PRE(前导码)
  4. localparam st_start = 6'b00_0100; //开始状态,发送ST(开始)+OP(操作码)
  5. localparam st_addr = 6'b00_1000; //写地址,发送PHY地址+寄存器地址
  6. localparam st_wr_data = 6'b01_0000; //TA+写数据
  7. localparam st_rd_data = 6'b10_0000; //TA+读数据
  1. //(三段式状态机)同步时序描述状态转移
  2. always @(posedge dri_clk or negedge rst_n) begin
  3. if(!rst_n)
  4. cur_state <= st_idle;
  5. else
  6. cur_state <= next_state;
  7. end

                状态转移,当前状态等于下一状态

                        ⑤状态机——状态转移条件
  1. //组合逻辑判断状态转移条件
  2. always @(*) begin
  3. next_state = st_idle;
  4. case(cur_state)
  5. st_idle : begin
  6. if(op_exec)
  7. next_state = st_pre;
  8. else
  9. next_state = st_idle;
  10. end
  11. st_pre : begin
  12. if(st_done)
  13. next_state = st_start;
  14. else
  15. next_state = st_pre;
  16. end
  17. st_start : begin
  18. if(st_done)
  19. next_state = st_addr;
  20. else
  21. next_state = st_start;
  22. end
  23. st_addr : begin
  24. if(st_done) begin
  25. if(op_code == 2'b01) //MDIO接口写操作
  26. next_state = st_wr_data;
  27. else
  28. next_state = st_rd_data; //MDIO接口读操作
  29. end
  30. else
  31. next_state = st_addr;
  32. end
  33. st_wr_data : begin
  34. if(st_done)
  35. next_state = st_idle;
  36. else
  37. next_state = st_wr_data;
  38. end
  39. st_rd_data : begin
  40. if(st_done)
  41. next_state = st_idle;
  42. else
  43. next_state = st_rd_data;
  44. end
  45. default : next_state = st_idle;
  46. endcase
  47. end

        在 st_idle(空闲状态)模式下:op_exec(触发开始信号)拉高,下一状态进入st_pre,否则保持。

        在st_pre(发送PRE(前导码))模式下:st_done(状态开始跳转信号)拉高,下一状态进入st_start,否则保持

        在st_start(开始状态,发送ST(开始)+OP(操作码))模式下:st_done(状态开始跳转信号)拉高,下一状态进入st_addr,否则保持

        在st_addr(写地址,发送PHY地址+寄存器地址)模式下:如果op_code (操作码)== 2'b01,下一状态进入st_wr_data,否则进入st_rd_data

        在st_wr_data(TA+写数据)模式下:st_done(状态开始跳转信号)拉高,下一状态进入st_idle,否则保持

        在st_rd_data(TA+读数据)模式下:st_done(状态开始跳转信号)拉高,下一状态进入st_idle,否则保持

                        ⑥状态机——状态输出
  1. //时序电路描述状态输出
  2. always @(posedge dri_clk or negedge rst_n) begin
  3. if(!rst_n) begin
  4. cnt <= 5'd0;
  5. op_code <= 1'b0;
  6. addr_t <= 1'b0;
  7. wr_data_t <= 1'b0;
  8. rd_data_t <= 1'b0;
  9. op_done <= 1'b0;
  10. st_done <= 1'b0;
  11. op_rd_data <= 1'b0;
  12. op_rd_ack <= 1'b1;
  13. mdio_dir <= 1'b0;
  14. mdio_out <= 1'b1;
  15. end
  16. else begin
  17. st_done <= 1'b0 ;
  18. cnt <= cnt +1'b1 ;
  19. case(cur_state)
  20. st_idle : begin
  21. mdio_out <= 1'b1;
  22. mdio_dir <= 1'b0;
  23. op_done <= 1'b0;
  24. cnt <= 7'b0;
  25. if(op_exec) begin
  26. op_code <= {op_rh_wl,~op_rh_wl}; //OP_CODE: 2'b01(写) 2'b10(读)
  27. addr_t <= op_addr;
  28. wr_data_t <= op_wr_data;
  29. op_rd_ack <= 1'b1;
  30. end
  31. end
  32. st_pre : begin //发送前导码:321bit
  33. mdio_dir <= 1'b1; //切换MDIO引脚方向:输出
  34. mdio_out <= 1'b1; //MDIO引脚输出高电平
  35. if(cnt == 7'd62)
  36. st_done <= 1'b1;
  37. else if(cnt == 7'd63)
  38. cnt <= 7'b0;
  39. end
  40. st_start : begin
  41. case(cnt)
  42. 7'd1 : mdio_out <= 1'b0; //发送开始信号 2'b01
  43. 7'd3 : mdio_out <= 1'b1;
  44. 7'd5 : mdio_out <= op_code[1]; //发送操作码
  45. 7'd6 : st_done <= 1'b1;
  46. 7'd7 : begin
  47. mdio_out <= op_code[0];
  48. cnt <= 7'b0;
  49. end
  50. default : ;
  51. endcase
  52. end
  53. st_addr : begin
  54. case(cnt)
  55. 7'd1 : mdio_out <= PHY_ADDR[4]; //发送PHY地址
  56. 7'd3 : mdio_out <= PHY_ADDR[3];
  57. 7'd5 : mdio_out <= PHY_ADDR[2];
  58. 7'd7 : mdio_out <= PHY_ADDR[1];
  59. 7'd9 : mdio_out <= PHY_ADDR[0];
  60. 7'd11: mdio_out <= addr_t[4]; //发送寄存器地址
  61. 7'd13: mdio_out <= addr_t[3];
  62. 7'd15: mdio_out <= addr_t[2];
  63. 7'd17: mdio_out <= addr_t[1];
  64. 7'd18: st_done <= 1'b1;
  65. 7'd19: begin
  66. mdio_out <= addr_t[0];
  67. cnt <= 7'd0;
  68. end
  69. default : ;
  70. endcase
  71. end
  72. st_wr_data : begin
  73. case(cnt)
  74. 7'd1 : mdio_out <= 1'b1; //发送TA,写操作(2'b10)
  75. 7'd3 : mdio_out <= 1'b0;
  76. 7'd5 : mdio_out <= wr_data_t[15];//发送写寄存器数据
  77. 7'd7 : mdio_out <= wr_data_t[14];
  78. 7'd9 : mdio_out <= wr_data_t[13];
  79. 7'd11: mdio_out <= wr_data_t[12];
  80. 7'd13: mdio_out <= wr_data_t[11];
  81. 7'd15: mdio_out <= wr_data_t[10];
  82. 7'd17: mdio_out <= wr_data_t[9];
  83. 7'd19: mdio_out <= wr_data_t[8];
  84. 7'd21: mdio_out <= wr_data_t[7];
  85. 7'd23: mdio_out <= wr_data_t[6];
  86. 7'd25: mdio_out <= wr_data_t[5];
  87. 7'd27: mdio_out <= wr_data_t[4];
  88. 7'd29: mdio_out <= wr_data_t[3];
  89. 7'd31: mdio_out <= wr_data_t[2];
  90. 7'd33: mdio_out <= wr_data_t[1];
  91. 7'd35: mdio_out <= wr_data_t[0];
  92. 7'd37: begin
  93. mdio_dir <= 1'b0;
  94. mdio_out <= 1'b1;
  95. end
  96. 7'd39: st_done <= 1'b1;
  97. 7'd40: begin
  98. cnt <= 7'b0;
  99. op_done <= 1'b1; //写操作完成,拉高op_done信号
  100. end
  101. default : ;
  102. endcase
  103. end
  104. st_rd_data : begin
  105. case(cnt)
  106. 7'd1 : begin
  107. mdio_dir <= 1'b0; //MDIO引脚切换至输入状态
  108. mdio_out <= 1'b1;
  109. end
  110. 7'd2 : ; //TA[1]位,该位为高阻状态,不操作
  111. 7'd4 : op_rd_ack <= mdio_in; //TA[0]位,0(应答) 1(未应答)
  112. 7'd6 : rd_data_t[15] <= mdio_in; //接收寄存器数据
  113. 7'd8 : rd_data_t[14] <= mdio_in;
  114. 7'd10: rd_data_t[13] <= mdio_in;
  115. 7'd12: rd_data_t[12] <= mdio_in;
  116. 7'd14: rd_data_t[11] <= mdio_in;
  117. 7'd16: rd_data_t[10] <= mdio_in;
  118. 7'd18: rd_data_t[9] <= mdio_in;
  119. 7'd20: rd_data_t[8] <= mdio_in;
  120. 7'd22: rd_data_t[7] <= mdio_in;
  121. 7'd24: rd_data_t[6] <= mdio_in;
  122. 7'd26: rd_data_t[5] <= mdio_in;
  123. 7'd28: rd_data_t[4] <= mdio_in;
  124. 7'd30: rd_data_t[3] <= mdio_in;
  125. 7'd32: rd_data_t[2] <= mdio_in;
  126. 7'd34: rd_data_t[1] <= mdio_in;
  127. 7'd36: rd_data_t[0] <= mdio_in;
  128. 7'd39: st_done <= 1'b1;
  129. 7'd40: begin
  130. op_done <= 1'b1; //读操作完成,拉高op_done信号
  131. op_rd_data <= rd_data_t;
  132. rd_data_t <= 16'd0;
  133. cnt <= 7'd0;
  134. end
  135. default : ;
  136. endcase
  137. end
  138. default : ;
  139. endcase
  140. end
  141. end
  142. endmodule

                复位后:cnt(计数器)、op_code(操作码  2'b01(写)  2'b10(读))、addr_t(缓存寄存器地址)、wr_data_t(缓存写寄存器的数据)、rd_data_t(缓存读寄存器数据)、op_done(读写完成)、st_done(状态开始跳转信号)、op_rd_data(读出的数据)、mdio_dir(MDIO数据(SDA)方向控制)清零。op_rd_ack(读应答信号 0:应答 1:未应答)、mdio_out(MDIO输出信号)置1。

        mdio_out(MDIO输出信号)置1、mdio_dir(MDIO数据(SDA)方向控制):输入、op_done(读写完成)置0。

        如果op_exec(触发信号)拉高,对op_code进行为拼接赋值,op_addr、op_wr_data进行寄存,op_rd_ack(读应答信号 0:应答 1:未应答)置1。

        dri_clk下计数63个上升沿,对应MDC时钟31个上升沿,即为发送32个前导码1。记满cnt清零

        发送起始信号ST01,发送读写操作码低电平写,op_code[1],op_code[0]低电平写,高电平读。st_done(状态开始跳转信号)置为1。        

        为什么cnt计数为奇数时改变数据:dri_clk为MDC的2倍,奇数刚好在MDC下降沿改变数据。

        依次发送PHY地址、寄存器地址,期间st_done(状态开始跳转信号)置为1

        发送TA写指令后,开始发送写入数据。之后将mdio_dir(MDIO数据(SDA)方向控制)置0释放总线进入高阻,mdio_out(MDIO输出信号)置1,op_done(读写完成)拉高标志一次读写造作结束。

 

         发送TA读指令后,TA[1]位无需操作保持高阻状态,由PHY侧控制总线。检测TA[0]位,如有应答,PHY侧会拉低mdio_in引脚,作为应答信号。依次接受16位读出数据寄存到rd_data_t下。之后op_done(读写完成)拉高标志一次读写造作结束,op_rd_data <= rd_data_t;进行输出其他新信号清零。

                (2)mdio_ctrl模块

  1. module mdio_ctrl(
  2. input clk ,
  3. input rst_n ,
  4. input soft_rst_trig , //软复位触发信号
  5. input op_done , //读写完成
  6. input [15:0] op_rd_data , //读出的数据
  7. input op_rd_ack , //读应答信号 0:应答 1:未应答
  8. output reg op_exec , //触发开始信号
  9. output reg op_rh_wl , //低电平写,高电平读
  10. output reg [4:0] op_addr , //寄存器地址
  11. output reg [15:0] op_wr_data , //写入寄存器的数据
  12. output [1:0] led //LED灯指示以太网连接状态
  13. );
  14. //reg define
  15. reg rst_trig_d0;
  16. reg rst_trig_d1;
  17. reg rst_trig_flag; //soft_rst_trig信号触发标志
  18. reg [23:0] timer_cnt; //定时计数器
  19. reg timer_done; //定时完成信号
  20. reg start_next; //开始读下一个寄存器标致
  21. reg read_next; //处于读下一个寄存器的过程
  22. reg link_error; //链路断开或者自协商未完成
  23. reg [2:0] flow_cnt; //流程控制计数器
  24. reg [1:0] speed_status; //连接速率
  25. //wire define
  26. wire pos_rst_trig; //soft_rst_trig信号上升沿
  27. //采soft_rst_trig信号上升沿
  28. assign pos_rst_trig = ~rst_trig_d1 & rst_trig_d0;
  29. //未连接或连接失败时led赋值00
  30. // 01:10Mbps 10:100Mbps 11:1000Mbps 00:其他情况
  31. assign led = link_error ? 2'b00: speed_status;
  32. //对soft_rst_trig信号延时打拍
  33. always @(posedge clk or negedge rst_n) begin
  34. if(!rst_n) begin
  35. rst_trig_d0 <= 1'b0;
  36. rst_trig_d1 <= 1'b0;
  37. end
  38. else begin
  39. rst_trig_d0 <= soft_rst_trig;
  40. rst_trig_d1 <= rst_trig_d0;
  41. end
  42. end
  43. //定时计数
  44. always @(posedge clk or negedge rst_n) begin
  45. if(!rst_n) begin
  46. timer_cnt <= 1'b0;
  47. timer_done <= 1'b0;
  48. end
  49. else begin
  50. if(timer_cnt == 24'd1_000_000 - 1'b1) begin
  51. timer_done <= 1'b1;
  52. timer_cnt <= 1'b0;
  53. end
  54. else begin
  55. timer_done <= 1'b0;
  56. timer_cnt <= timer_cnt + 1'b1;
  57. end
  58. end
  59. end
  60. //根据软复位信号对MDIO接口进行软复位,并定时读取以太网的连接状态
  61. always @(posedge clk or negedge rst_n) begin
  62. if(!rst_n) begin
  63. flow_cnt <= 3'd0;
  64. rst_trig_flag <= 1'b0;
  65. speed_status <= 2'b00;
  66. op_exec <= 1'b0;
  67. op_rh_wl <= 1'b0;
  68. op_addr <= 1'b0;
  69. op_wr_data <= 1'b0;
  70. start_next <= 1'b0;
  71. read_next <= 1'b0;
  72. link_error <= 1'b0;
  73. end
  74. else begin
  75. op_exec <= 1'b0;
  76. if(pos_rst_trig)
  77. rst_trig_flag <= 1'b1; //拉高软复位触发标志
  78. case(flow_cnt)
  79. 2'd0 : begin
  80. if(rst_trig_flag) begin //开始对MDIO接口进行软复位
  81. op_exec <= 1'b1;
  82. op_rh_wl <= 1'b0;
  83. op_addr <= 5'h00;
  84. op_wr_data <= 16'h9140; //Bit[15]=1'b1,表示软复位
  85. flow_cnt <= 3'd1;
  86. end
  87. else if(timer_done) begin //定时完成,获取以太网连接状态
  88. op_exec <= 1'b1;
  89. op_rh_wl <= 1'b1;
  90. op_addr <= 5'h01;
  91. flow_cnt <= 3'd2;
  92. end
  93. else if(start_next) begin //开始读下一个寄存器,获取以太网通信速度
  94. op_exec <= 1'b1;
  95. op_rh_wl <= 1'b1;
  96. op_addr <= 5'h11;
  97. flow_cnt <= 3'd2;
  98. start_next <= 1'b0;
  99. read_next <= 1'b1;
  100. end
  101. end
  102. 2'd1 : begin
  103. if(op_done) begin //MDIO接口软复位完成
  104. flow_cnt <= 3'd0;
  105. rst_trig_flag <= 1'b0;
  106. end
  107. end
  108. 2'd2 : begin
  109. if(op_done) begin //MDIO接口读操作完成
  110. if(op_rd_ack == 1'b0 && read_next == 1'b0) //读第一个寄存器,接口成功应答,
  111. flow_cnt <= 3'd3; //读第下一个寄存器,接口成功应答
  112. else if(op_rd_ack == 1'b0 && read_next == 1'b1)begin
  113. read_next <= 1'b0;
  114. flow_cnt <= 3'd4;
  115. end
  116. else begin
  117. flow_cnt <= 3'd0;
  118. end
  119. end
  120. end
  121. 2'd3 : begin
  122. flow_cnt <= 3'd0; //链路正常并且自协商完成
  123. if(op_rd_data[5] == 1'b1 && op_rd_data[2] == 1'b1)begin
  124. start_next <= 1;
  125. link_error <= 0;
  126. end
  127. else begin
  128. link_error <= 1'b1;
  129. end
  130. end
  131. 3'd4: begin
  132. flow_cnt <= 3'd0;
  133. if(op_rd_data[15:14] == 2'b10)
  134. speed_status <= 2'b11; //1000Mbps
  135. else if(op_rd_data[15:14] == 2'b01)
  136. speed_status <= 2'b10; //100Mbps
  137. else if(op_rd_data[15:14] == 2'b00)
  138. speed_status <= 2'b01; //10Mbps
  139. else
  140. speed_status <= 2'b00; //其他情况
  141. end
  142. endcase
  143. end
  144. end
  145. endmodule
                        ① 对soft_rst_trig信号延时打两拍

        对soft_rst_trig 软复位触发信号进行打两拍,抓取soft_rst_trig上升沿信号

                         ②定时计数

                         ③流程控制

         如果检测到soft_rst_trig 上升沿,将rst_trig_flag信号拉高

         不拉高op_exec原因:触摸按键按下实践不确定,如果在读写期间按下,拉高op_exec没有意义,无法达到复位效果。

        0状态:

        如果rst_trig_flag信号拉高,进行软复位,op_exec(触发开始信号)置1,op_rh_wl(低电平写,高电平读)置0。op_addr(寄存器地址)给出寄存器地址0x00,op_wr_data(写入寄存器的数据)对PHY芯片寄存器给出配置。flow_cnt下一状态进入1状态。

                16'h9140   =    2'b1001_0001_0100_0000‬

回看0x00寄存器,

                如果复位没有按下,则会等待计数器记满后,读取当前通信速率。op_exec(触发开始信号)置1,op_rh_wl(低电平写,高电平读)置1,op_addr(寄存器地址)给出寄存器地址0x01。flow_cnt下一状态进入2状态。

                16'h9140   =    2'b1001_0001_0100_0000‬

                如果计数器没有记满,start_next拉高时,op_exec(触发开始信号)置1,op_rh_wl(低电平写,高电平读)置1,op_addr(寄存器地址)给出寄存器地址0x11。flow_cnt下一状态进入2状态。start_next <= 1'b0;read_next <= 1'b1; 

        1状态:

        等待op_done读写结束标志拉高,下一状态为0状态,rst_trig_flag标志位拉低。

        2状态:

        如果应答信号op_rd_ack为0,read_next(寄存器读取标志)为0,跳转到3状态。

        如果read_next(寄存器读取标志)为1,重置read_next为0,下一状态为4状态。

        如果没有应答信号op_rd_ack为1时,下一状态进入0状态。

        3状态:

下一状态进入0状态。读出0x01寄存器的第5位和第2位,进行判断。符合条件(远端连接成功)start_next拉高。

         4状态:

        下一状态进入0状态,读取寄存器0x11的15、14位,的出对应的通信速率,赋值给speed_status。

        利用三目运算为led进行赋值。

                (3)mdio_rw_test顶层

  1. module mdio_rw_test(
  2. input sys_clk ,
  3. input sys_rst_n,
  4. //MDIO接口
  5. output eth_mdc , //PHY管理接口的时钟信号
  6. inout eth_mdio , //PHY管理接口的双向数据信号
  7. output eth_rst_n, //以太网复位信号
  8. input touch_key, //触摸按键
  9. output [1:0] led //LED连接速率指示
  10. );
  11. //wire define
  12. wire op_exec ; //触发开始信号
  13. wire op_rh_wl ; //低电平写,高电平读
  14. wire [4:0] op_addr ; //寄存器地址
  15. wire [15:0] op_wr_data ; //写入寄存器的数据
  16. wire op_done ; //读写完成
  17. wire [15:0] op_rd_data ; //读出的数据
  18. wire op_rd_ack ; //读应答信号 0:应答 1:未应答
  19. wire dri_clk ; //驱动时钟
  20. //硬件复位
  21. assign eth_rst_n = sys_rst_n;
  22. //MDIO接口驱动
  23. mdio_dri #(
  24. .PHY_ADDR (5'h04), //PHY地址
  25. .CLK_DIV (6'd10) //分频系数
  26. )
  27. u_mdio_dri(
  28. .clk (sys_clk),
  29. .rst_n (sys_rst_n),
  30. .op_exec (op_exec ),
  31. .op_rh_wl (op_rh_wl ),
  32. .op_addr (op_addr ),
  33. .op_wr_data (op_wr_data),
  34. .op_done (op_done ),
  35. .op_rd_data (op_rd_data),
  36. .op_rd_ack (op_rd_ack ),
  37. .dri_clk (dri_clk ),
  38. .eth_mdc (eth_mdc ),
  39. .eth_mdio (eth_mdio )
  40. );
  41. //MDIO接口读写控制
  42. mdio_ctrl u_mdio_ctrl(
  43. .clk (dri_clk),
  44. .rst_n (sys_rst_n ),
  45. .soft_rst_trig (touch_key ),
  46. .op_done (op_done ),
  47. .op_rd_data (op_rd_data),
  48. .op_rd_ack (op_rd_ack ),
  49. .op_exec (op_exec ),
  50. .op_rh_wl (op_rh_wl ),
  51. .op_addr (op_addr ),
  52. .op_wr_data (op_wr_data),
  53. .led (led )
  54. );
  55. endmodule

六、下载验证

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/575883
推荐阅读
相关标签
  

闽ICP备14008679号