verilog 实现DMIO读写phy芯片
verilog DMIO 读写 phy芯片
phy_test
`timescale 1ns / 1ps
/**
-------------main code--------------------------
对 FPGA 读写 PHY(YT8511 芯片)芯片
通过 MDIO 对以太网PHY芯片的配置读取和设置
一、phy 地址
1. phy构成
YT8511 PHY 地址: YT8511 芯片的 PHY 地址由 LED_ACT 和 RXD[1:0]引脚决定,
PHY 地址一共有五位,其中高两位固定为 00,LED_ACT 和 RXD[1:0]引脚表示低三位,
我们可以通过硬件电路设置 LED_ACT 和RXD[1:0]引脚引脚为上拉或者下拉,
即分配为高低电平,0 或 1,从而表示不同的地址。
2. phy 地址 00100
根据电路图可知, RXD0/RXD1 接的下拉电阻(为何又接了上拉电阻), ENET_ACT 接的上拉电阻
所以
二、phy 复位键解释
1. 芯片复位功能
YT8511 芯片复位之后, PHY内部寄存器的数据会恢复默认的状态,并且重新开始和MAC进行自协商
2. 芯片复位方式
1) 硬件复位: 持续 拉低 PHY_RST_N 10ms 即可对芯片进行复位
2) 软件复位: 向寄存器地址 0x00 的 bit[15]写入 1 进行复位, 复位之后,该位置自动清零
三、phy 寄存器
YT8511 共有22位寄存器,
1. 控制寄存器: 用于控制选择芯片的复位、回环、读写速率、 自动协商等等
2. 状态寄存器: 自动协商状态状态, 网络连接状态(Link_status)等
3. PHY芯片特定状态寄存器: 网络连接速率等
....
四、phy mdio读取步骤
YT8511(国产phy) / RTL8211E(进口phy), 进口的文档很详细
1.按键按下 key1, 并锁存值, 然后进行 开始对phy 芯片复位
2.复位需要持续拉低 phy_rst 10ms, 这里延迟了 100ms
3.然后给定 MDC 时钟, 这里延迟了 100ms开始读取值, 具体时间没有测试,实际需要延迟的值肯定小于这个
4.然后开始对操作 MDIO, 观察 MDIO 读出对应芯片的值
五、踩坑
1. 特别需要注意的点就是, 根据时序图查看, 数据和时钟是错开的,也就是再MDC时钟上升沿开始采数, 然后对应的 DMIO一定要稳定
2. 然后要注意复位信号拉低的时间延迟
3. 还需要注意 phy 芯片地址和寄存器地址的读取以及和定义(芯片手册),
4. 当然代码也很乱, 第一次写,不懂状态机,自由发挥写的,总算是弄出来了
*/
module phy_test (
input sys_clk,
// input sys_rst,
input key, //按键值
inout phy_mdio, //PHY MDIO 接口数据配置
output phy_mdc, //PHY管理接口的时钟信号
output phy_rst_n, //复位键
output [1:0] led //灯的状态
);
reg sys_rst = 1'b1;
wire key_val;
key_debounce u_key_debounce(
.sys_clk (sys_clk),
.sys_rst (sys_rst),
.key (key),
.key_val (key_val)
);
reg check_key_val = 1'b0;
always @(posedge sys_clk) begin
if(key_val == 1'b1)
check_key_val = 1'b1;
else
check_key_val = check_key_val;
end
parameter DELAY_TIME = 27'd1_0000_0000;
reg phy_rst = 1'b0; //上电的时候给个低电平
assign phy_rst_n = phy_rst;
reg led0 = 1'b0;
assign led[0] = led0;
wire led_status;
assign led_status = key_val ? 1'b1: 1'b0;
always @(posedge sys_clk) begin
if(led_status == 1'b1) begin
led0 <= 1'b1;
phy_rst <= 1'b1;
end else begin
phy_rst <= phy_rst;
led0 <= led0;
end
end
read_phy #(
.DELAY_TIME (DELAY_TIME)
) u_read_phy(
.sys_clk (sys_clk),
.phy_rst_n (phy_rst),
.phy_mdc (phy_mdc),
.phy_mdio (phy_mdio)
);
endmodule //phy_test
read_phy
`timescale 1ns / 1ps
module read_phy (
input sys_clk,
input phy_rst_n,
output phy_mdc, //phy时钟管理, 最大不能超过 12.5Mhz(芯片定义), 也就是不能超过 80ns,所以给定4分频
inout phy_mdio
);
wire phy_rst;
assign phy_rst = phy_rst_n;
parameter DELAY_TIME = 27'd1_0000_0000;
// localparam DELAY_TIME = 27'd400;
/*-----------------------------复位延迟计时器----------------------------------*/
reg [26:0] phy_rst_cnt = 1'b0;
reg phy_rst_delay = 1'b0;
always @(posedge sys_clk) begin
if(!phy_rst) begin
phy_rst_delay <= 1'b0;
phy_rst_cnt <= 1'b0;
end else if(phy_rst_cnt < DELAY_TIME) begin
if(phy_rst_delay == 1'b0) begin
phy_rst_cnt <= phy_rst_cnt + 1'b1;
end else begin
phy_rst_cnt <= phy_rst_cnt;
end
end else begin
phy_rst_delay <= 1'b1;
end
end
/*-----------------------------复位延迟计时器----------------------------------*/
/*-----------------------------给定 MDC延时计时器----------------------------------*/
reg [26:0] mdc_delay_cnt = 1'b0;
reg mdc_delay = 1'b0;
always @(posedge sys_clk) begin
if(!phy_rst_delay) begin
mdc_delay <= 1'b0;
mdc_delay_cnt <= 1'b0;
end else if(mdc_delay_cnt < DELAY_TIME) begin
if(mdc_delay == 1'b0) begin
mdc_delay_cnt <= mdc_delay_cnt + 1'b1;
end else begin
mdc_delay_cnt <= mdc_delay_cnt;
end
end else begin
mdc_delay <= 1'b1;
end
end
/*-----------------------------复位延迟计时器----------------------------------*/
/*-----------------------------时钟分频----------------------------------*/
parameter CLK_DIV = 8; //分频系数 2*当前值 = 最终分频
reg _clk_freq = 1'b1;
reg [5:0] freq_cnt = 1'b0; //分频计数器, 支持 2,4,8,16 分频
wire [5:0] _freq_cnt;
assign _freq_cnt = CLK_DIV >> 1; //分频除以二, 去中取反
always @(posedge sys_clk) begin
if(!phy_rst_delay) begin
freq_cnt <= 1'b0;
_clk_freq <= 1'b1;
end else if(freq_cnt < _freq_cnt - 1) begin //在中的时候取反
freq_cnt <= freq_cnt + 1'b1;
end else begin
freq_cnt <= 1'b0;
_clk_freq = ~_clk_freq;
end
end
/*-----------------------------分频计时----------------------------------*/
reg [5:0] div_cnt = 6'b0;
always @(posedge sys_clk) begin
if(!phy_rst_delay) begin
div_cnt <= 6'b0;
end else if(div_cnt == CLK_DIV - 1) begin
div_cnt <= 6'b0;
end else begin
div_cnt <= div_cnt + 1'b1;
end
end
/*-----------------------------时钟分频----------------------------------*/
/*-----------------------------MDC时钟----------------------------------*/
assign phy_mdc = _clk_freq; //80ns 12.5MHz
/*-----------------------------MDC时钟----------------------------------*/
/*-----------------------------MDIO写数据----------------------------------*/
parameter PHY_ADDR = 5'b00100; //物理地址
reg is_out = 1'b1; //决定inout是input还是output
reg mdio_out = 1'b1;
wire mdio_in; //用于向 mdio_in 输出数据
assign phy_mdio = is_out ? 1'bz : mdio_out;
assign mdio_in = phy_mdio;
/*
1. 写入 PRE 32bit 1
2. 写入 START 2bit 01 帧开始信号
3. 写入 READ_OP 2bit 操作码 读: 10, 写: 01
4. 写入 PHY_addr 5bit 00100
5. 写入 REG寄存器 5bit 表示32位寄存器
6. 写入 TA 2bit 转向, 读的话, 2'bZ0, MDIO由PHY驱动, 写的话 2'b10, 还是由MAC地址驱动
7. 读写 DATA 16bit 高位在前,地位在后
8. 空闲状态IDLE Z MDIO无源驱动,高阻状态
*/
reg [5:0] send_cnt = 6'b0; //计时器, 总共处理 40bit 次
localparam SEND_TOTAL_CNT = 6'd46; //总共发送的次数
localparam INDEX_PRE = 6'd0; //操作位的地址
localparam INDEX_STAR_OP = INDEX_PRE + 6'd32; //操作位的地址
localparam INDEX_READ_OP = INDEX_STAR_OP + 6'd2; //操作位的地址
localparam INDEX_PHY_ADD = INDEX_READ_OP + 6'd2; //操作位的地址
localparam INDEX_REG_VAL = INDEX_PHY_ADD + 6'd5; //操作位的地址
reg [SEND_TOTAL_CNT-1:0] out_data; //操作指令 [pre ` TA]
reg [6:0] index = 1'b0;
reg is_start = 1'b0; //开始指令读写操作
always @(posedge sys_clk) begin
if(mdc_delay && div_cnt == (_freq_cnt - 1)) begin
if(is_start == 1'b0) begin
send_cnt <= 1'b0;
is_out <= 1'b0;
is_start <= 1'b1;
out_data <= 46'b1111_1111_1111_1111_1111_1111_1111_1111_01_10_00100_00001;
end else if(is_out == 1'b0 && send_cnt <= 6'd45) begin
index = SEND_TOTAL_CNT - send_cnt - 1;
mdio_out <= out_data[index];
send_cnt <= send_cnt + 1'b1;
if(send_cnt == 6'd45) begin
is_out <= 1'b1;
end
end else begin
mdio_out <= 1'b1;
end
end
end
vio_0 vio_0 (
.clk(sys_clk), // input wire clk
.probe_in0(out_data) // input wire [25 : 0] probe_out0
);
ila_0 ila_0 (
.clk(sys_clk), // input wire clk
.probe0(mdio_out), // input wire [0:0] probe0
.probe1(mdio_in), // input wire [0:0] probe1
.probe2(_clk_freq), // input wire [0:0] probe2
.probe3(mdc_delay), // input wire [0:0] probe3
.probe4(send_cnt) // input wire [5:0] probe4
);
endmodule //read_phy
仿真 phy_test_sim
`timescale 1ns / 1ps
module phy_test_sim ();
reg sys_clk;
reg sys_rst;
wire phy_mdc;
reg phy_mdio;
reg key;
initial begin
sys_clk = 1'b0;
sys_rst = 1'b0;
key = 1'b0;
#200 key = 1'b1;
// #200 key = 1'b0;
end
always #10 sys_clk = ~sys_clk;
parameter DELAY_TIME = 27'd500;
phy_test #(
.DELAY_TIME (DELAY_TIME)
) u_phy_test(
.sys_clk (sys_clk),
// .sys_rst (sys_rst),
.key (key)
);
endmodule
本文来自博客园踩坑狭,作者:韩若明瞳,转载请注明原文链接:https://www.cnblogs.com/han-guang-xue/p/16643509.html

浙公网安备 33010602011771号